访问C#中的已处置封闭?

访问C#中的已处置封闭?,c#,.net,multithreading,C#,.net,Multithreading,我正在调查Microsoft企业库(数据应用程序块)——示例sln 它们有一个异步读取数据的示例(IAsync,尽管新版本(6)也支持async) 但是Resharper(或visualstudio-nevermind)向我显示:“访问已处理的闭包”:(首先我将显示图像,这样图像会更清晰,然后我将粘贴代码) 代码: /*1*/ [Description("Execute a command that retrieves data asynchronously")] /*2*/ st

我正在调查Microsoft企业库(数据应用程序块)——示例sln

它们有一个异步读取数据的示例(
IAsync
,尽管新版本(6)也支持
async

但是Resharper(或visualstudio-nevermind)向我显示:“访问已处理的闭包”:(首先我将显示图像,这样图像会更清晰,然后我将粘贴代码)

代码:

/*1*/    [Description("Execute a command that retrieves data asynchronously")]
/*2*/    static void ReadDataAsynchronously()
/*3*/    {
/*4*/        if (!SupportsAsync(asyncDB)) return;
/*5*/   
/*6*/        using(var doneWaitingEvent = new ManualResetEvent(false))
/*7*/        using(var readCompleteEvent = new ManualResetEvent(false))
/*8*/        {
/*9*/            try
/*10*/            {
/*11*/                // Create command to execute stored procedure and add parameters
/*12*/                DbCommand cmd = asyncDB.GetStoredProcCommand("ListOrdersSlowly");
/*13*/                asyncDB.AddInParameter(cmd, "state", DbType.String, "Colorado");
/*14*/                asyncDB.AddInParameter(cmd, "status", DbType.String, "DRAFT");
/*15*/                // Execute the query asynchronously specifying the command and the
/*16*/                // expression to execute when the data access process completes.
/*17*/                asyncDB.BeginExecuteReader(cmd,
/*18*/                    asyncResult = >
/*19*/                    {
/*20*/                        // Lambda expression executed when the data access completes.
/*21*/                        doneWaitingEvent.Set();
/*22*/                        try
/*23*/                        {
/*24*/                            using(IDataReader reader = asyncDB.EndExecuteReader(asyncResult))
/*25*/                            {
/*26*/                                Console.WriteLine();
/*27*/                                Console.WriteLine();
/*28*/                                DisplayRowValues(reader);
/*29*/                            }
/*30*/                        }
/*31*/                        catch (Exception ex)
/*32*/                        {
/*33*/                            Console.WriteLine("Error after data access completed: {0}", ex.Message);
/*34*/                        }
/*35*/                        finally
/*36*/                        {
/*37*/                            readCompleteEvent.Set();
/*38*/                        }
/*39*/                    }, null);
/*40*/   
/*41*/                // Display waiting messages to indicate executing asynchronouly
/*42*/                while (!doneWaitingEvent.WaitOne(1000))
/*43*/                {
/*44*/                    Console.Write("Waiting... ");
/*45*/                }
/*46*/   
/*47*/                // Allow async thread to write results before displaying "continue" prompt
/*48*/                readCompleteEvent.WaitOne();
/*49*/            }
/*50*/            catch (Exception ex)
/*51*/            {
/*52*/                Console.WriteLine("Error while starting data access: {0}", ex.Message);
/*53*/            }
/*54*/        }
/*55*/    }
问题:

为什么要发出这样的警告?有一个
manualreset checked signal
(在循环中运行),它阻止
使用
子句到达-这意味着-no
dispose
将调用


那么它为什么会大叫(警告)?

您将
doneWaitingEvent
传递给可能超出使用块范围的lambda。也就是说,当lambda执行时,
Dispose
有被调用的风险

它会发出警告,因为引擎不够聪明,无法在委托代码完成之前确定using块永远不会退出。这就是为什么这是一个警告而不是错误

您可以安全地忽略此警告,您可以让resharper通过使用特殊注释包装行来抑制此警告

asyncDB.BeginExecuteReader(cmd, asyncResult =>
{
    // Lambda expression executed when the data access completes.
    // ReSharper disable AccessToDisposedClosure
    doneWaitingEvent.Set();
    // ReSharper restore AccessToDisposedClosure
    try
    {
        using (IDataReader reader = asyncDB.EndExecuteReader(asyncResult))
        {
            Console.WriteLine();
            Console.WriteLine();
            DisplayRowValues(reader);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error after data access completed: {0}", ex.Message);
    }
    finally
    {
        // ReSharper disable AccessToDisposedClosure
        readCompleteEvent.Set();
        // ReSharper restore AccessToDisposedClosure
    }
}, null);

您看到ReSharper警告的原因是ReSharper的代码流分析引擎不够强大,无法看到正在发生的事情:它们假设您的代码可以使用
子句到达
的末尾,而不设置
doneWaitingEvent
,这是不可能的,因为
while
循环:

while (!doneWaitingEvent.WaitOne(1000)) {
    Console.Write("Waiting... ");
}
循环将继续打印
“Waiting…”
行,直到
doneWaitingEvent.Set(),使用
块阻止代码到达
的末尾。另一个警告也是如此


长话短说,这个警告可以被安全地忽略。添加ReSharper的“忽略此警告”注释,并可以选择与它们一起提交错误报告。

旁白:为什么在等待结果时使用异步调用?似乎在这里使用
Task
s会更简单。此SLN还包含15个同样使用任务(异步)的示例。但这不是我的问题。我很好奇——有没有更简单的情况,R#足够聪明地认为是“安全的”?如果是这样,我会感到惊讶。我的猜测只是R#并不完美。它无法知道(即,它的静态分析不够高级,无法知道事件如何与异步调用交互)在
using
超出范围后从未调用lambda,因此它警告您可能会发生这种情况。@RoyiNamir示例可能是正确的。可能是的。R#试图阻止在使用
块完成
后调用
event.Set()
。通过异步调用或在lambda中使用该对象,这在理论上是可能的。警告并不意味着“这是一个bug”,但是“这可能是一个bug,你应该更仔细地看一下这段代码”对于未来的读者,在“有选择地向他们提交bug报告”上,不要认为“哦,他们是一家大公司,不会听我的”而否定这个想法。我收到了一个类似的“不必要的警告”,建议我应该使用不同的
CreateLinkedCancellationTokenSource
重载,我报告了它,并且@ScottChamberlain我认为这取决于。。。很久以前,我已经创建了两个非常严重的bug,但它们仍然没有修复,和。看起来他们修复了第二个,第一个被分配(给修复第二个的同一个人)。他们不仅仅是无视他们:)@ScottChamberlain。我也填充了一个bug,Resharper团队在不到2周的时间内修复了它。或者,如果你控制了被调用的函数,你可以使用JetBrains的
[InstantHandle]
注释来标记它对所有不同的调用都是安全的@我相信,在最近的一个版本中,jtb是resharper的新成员。请随意发布带有注释的答案。我赞成你也可以使用“ReSharper disable once AccessToDisposedClosure”来避免第二条评论