Access to disposed closure in C#?


3 Answers

It yells warning because the engine is not smart enough to figure out that the using block will never be exited before the delegate code completes. That is why this is a warning not a error.

You can safely ignore this warning, you can have resharper suppress the warning by wrapping the lines with special comments

                    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);
Question

I'm investigating Microsoft enterprise library (data application block) -- The samples sln.

They have a sample of reading data asynchronously ( IAsync , although the new ver (6) also support async).

But Resharper(or visual studio- nevermind) shows me : "Access to disposed closure" : (first i will show the image , so it will be clearer , then i'll paste the code )

code :

/*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*/    }

Question :

Why is it giving this warning ? there is a manualreset-checked-signal (which runs in a loop) which prevents the using clause to be reached - which means - no dispose will call .

So why does it yell (warning) ?




The reason you see ReSharper's warnings is that ReSharper's code flow analysis engine is not strong enough to see what is going on: they assume that your code could get to the end of the using clause without doneWaitingEvent being set, which is not possible due to a while loop:

while (!doneWaitingEvent.WaitOne(1000)) {
    Console.Write("Waiting... ");
}

The loop will keep printing the "Waiting... " line until doneWaitingEvent.Set(); gets called, preventing your code from reaching the end of the using block. Same goes for the other warning.

Long story short, this warning can be safely ignored. Add ReSharper's "ignore this warning" comments, and optionally file a bug report with them.




Access to disposed closure in C#?

You pass doneWaitingEvent to a lambda that may extend beyond the scope of the using block. I.e. there's a risk that Dispose will have been called when the lambda executes.




Is this ReSharper “Access to disposed closure” warning something to worry about?

This is something that has been a bug in ReSharper for quite a while, as described in their dev board. Unfortunately it's been an issue since 6.1.

Unfortunately, ReSharper analysis is not smart enough to track that query expression is definitely executed inside "using ..." block. For now, it tracks only in a single expression.

We are going to increase smartness in the future releases

Essentially, the analysis engine can only match one from up with one using within a single expression. It won't affect the functionality of the code; only the warning, and it's perfectly safe to ignore. Removing the .ToList() wouldn't affect the parsing of the expression tree.




Related