views:

1508

answers:

4

I've run ildasm to find that this:

    using(Simple simp = new Simple())
    {
        Console.WriteLine("here");
    }

generates IL code that is equivalent to this:

    Simple simp = new Simple();
    try
    {
        Console.WriteLine("here");
    }
    finally
    {
        if(simp != null)
        {
            simp.Dispose();
        }
    }

and the question is why the hell does it check null in the finally? The finally block will only be executed if the try block is executed, and the try block will only be executed if the Simple constructor succeeds (I.e. does not throw an exception), in which case simp will be non-null. (If there is some fear that some intervening steps might come between the Simple constructor and the beginning of the try block, then that would really be a problem because then an exception might be thrown that would prevent the finally block from executing at all.) So, why the hell?

Putting aside (please) the argument of whether the using statement is better than try-finally, I write my try-finally blocks as:

    Simple simp = new Simple();
    try
    {
        Console.WriteLine("here");
    }
    finally
    {
        simp.Dispose();
        simp = null;        // sanity-check in case I touch simp again
                            // because I don't rely on all classes
                            // necessarily throwing
                            // ObjectDisposedException
    }
+18  A: 

No, the finally block will ALWAYS be executed. You may not be getting the object from a new but from some other function that returns your object - and it might return NULL. using() is your friend!

dss539 was kind enough to suggest I include his note:

using(Simple simp = null)

is yet another reason that the expansion must check for null first.

n8wrl
Just to expand a little bit, you could be using the Factory Pattern and doing something like using (myFactory.CreateMyObject()) which could result in a null object.
Max Schmeling
If the constructor fails (before the try) though, the finally will not be executed.
Zifre
The assumption is that the 'new' will throw an exception if it fails, thus never returning a Null. If it throws the exception, the try block isn't even reached and the finally block does not get executed. So, the finally block is not ALWAYS executed.
spoulson
n8wrl
@n8 - you should edit my answer into yours so that the OP can accept yours as a complete answer.
dss539
the finally block will not always be executed. in special circumstances (ie program failures, crashes, power loss) code in finally will not be executed: so don't put any critical information in the finally block and expect it *always* to be executed :)
Makach
That is incorrect. If you never enter the try, the finally will not be executed.
Robin Clowers
+8  A: 

using(Simple simp = null) is yet another reason that the expansion must check for null first.

dss539
Why does that even compile? It is garanteed to be a runtime error.
Jonathan Allen
It's only guaranteed to be a runtime error if you use the simp object. Granted there's no reason for the using if you don't....
Max Schmeling
And while it is unlikely you would code = null explictly, it could be = SomeFactoryMethod() which could return null.
n8wrl
It compiles because I'm the developer and I damn well know what I'm doing. :P If I set it to null, I had a reason. Either that or I'm incompetent. In either case, I deserve to have the compiler respect my wishes. ;)
dss539
using (Simple simp = null) { if (someCondition) simp = new Simple(1); else if (otherCondition) simp = new Simple(2); ... } would dispose of simp in any case it exists.
configurator
@configurator - true but that's still a bit... dangerous. simp = new Simple(); and then another simp = new Simple(); and it would only dispose one of them
dss539
...and it does not compile: error CS1656: Cannot assign to 'simp' because it is a 'using variable'
Fredrik Mörk
@Fredrik - thanks. so the compiler enforces some form of invariance on it, either out of necessity or kindness.
dss539
+3  A: 

MSDN on the using statement.

What I think is strange is that it doesn't expand to:

Simple simp = new Simple();
Simple __compilergeneratedtmpname = simp;
try
{
    Console.WriteLine("here");
}
finally
{
    if(__compilergeneratedtmpname != null)
    {
        __compilergeneratedtmpname.Dispose();
    }
}
Dolphin
You would like to be able to change the `simp` variable within the using block??
Tom Hawtin - tackline
I didn't realize that the compiler enforces simp as effectively readonly.
Dolphin
Dolphin: That would cause problems when multithreading. When the constructor is immediately followed by the try, the CLR guarantees that no thread would 'interrupt' in the middle. Your way, there is no guarantee.
configurator
@configurator: Didn't realize that!
Jimmy
+1  A: 

It appears that your comment:

"If there is some fear that some intervening steps might come between the Simple constructor and the beginning of the try block, then that would really be a problem because then an exception might be thrown that would prevent the finally block from executing at all."

is possibly dead on. See:

Atomicity & Asynchronous Exception Failures

I also want to note the issue(s) with WCF and using:

Avoiding Problems with the Using Statement and WCF Service Proxies which references:

Avoiding Problems with the Using Statement

Joshua Drake