using (Stuff1 stf1 = new Stuff1(...)) // Allocation of stf1
using (Stuff2 stf2 = new Stuff2(...)) // Allocation of stf2
{
    try
    {
        // ... do stuff with stf1 and stf2 here ...
    }
    catch (Stuff1Exception ex1)
    {
        // ...
    }
    catch (Stuff2Exception ex2)
    {
        // ...
    }
} // Automatic deterministic destruction through Dispose() for stf1/stf2 - but in which order?

In other words, is stf2's Dispose() method guaranteed to be called first and then stf1's Dispose() method guaranteed to be called second? (basically: Dispose() methods being called in reverse order of the allocation of the object that they belong to?)

6

There are 6 answers

3
GregC On

No guarantee due to possible reordering. Dispose is called on a closing curly (or an implicit one in your example). You will always see the expected order in non-optimized Debug builds. You could use Thread.MemoryBarrier to force the order of operations.

using (Stuff1 stf1 = new Stuff1(...)) // Allocation of stf1
using (Stuff2 stf2 = new Stuff2(...)) // Allocation of stf2
{ {
    try
    {
        // ... do stuff with stf1 and stf2 here ...
    }
    catch (Stuff1Exception ex1)
    {
        // ...
    }
    catch (Stuff2Exception ex2)
    {
        // ...
    }
} Thread.MemoryBarrier(); }

Release-mode optimizations are done in such a way as to guarantee predictable results on this thread after all instructions complete. Everything else (view from a different thread on a separate core) is up for grabs. It's not against the rules to have a situation where stf2 is not yet disposed, but stf1 is disposed, as observed from another thread. Unless you force the order of operations. Try writing some asserts and running under Jinx

0
Andrew Barber On

Stuff2 will be disposed before Stuff1 because it is in an inner block.

Even though you didn't use braces on the outer block for Stuff1, it's still the same as if you did.

0
Joel Coehoorn On

using statements are no different than other block level statements. If you wrote code like this:

if (...)
    if (...)
    {

    }

It would be clear to you what order things take place (not that I would recommend that particular structure), because it's exactly the same as this:

if (...)
{
    if(...)
    {

    }
}

So it is with using. Your code is no different than the following:

using (...)
{
    using(...)
    {

    }
}

Here, it is perfectly clear that the inner using block terminates first, and so it's resource should be disposed first.

0
lontivero On

Yes, stf2.Dispose is called first and after that it is called stf1.Dispose

2
chemicalNova On

The using statement is converted to try-finally anyway. Its just syntactic sugar.. so you are correct in that your example would be translated as the following at compile time:

try
{
    Stuff1 stf1 = new Stuff1());
    try
    {
        Stuff2 stf2 = new Stuff2();
    }
    finally
    {
        stf2.Dispose();
    }
}
finally
{
    stf1.Dispose();
}
0
ojlovecd On

Yes, the Dispose method will be called in the end of the using scope, so Dispose() methods being called in reverse order of the allocation of the object that they belong to