26

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
    }
C. Ross
  • 31,137
  • 42
  • 147
  • 238
Matthew
  • 261
  • 3
  • 3
  • I got curious about one thing: how "expensive" is that extra sanity check (simp = null) in comparison to the compiler-generated sanity check, in terms of performance? In the end the difference between those two seem more philosophical than practical, but I may be mistaken. Interesting discussion either way. – Fredrik Mörk Jun 03 '09 at 21:04
  • @Fredrik - So you're asking whether "set to null" is faster/slower than "compare to null"? I'm not sure. Aside from that, one benefit of the using statement is you don't have to worry about that object being accessed outside of the using scope. (Unless you hold another reference to it.) – dss539 Jun 03 '09 at 21:35
  • 24
    "why the hell does it check null in the finally?" No good reason. Skipping the null check is an optimization which we could have performed. We didn't. Not really a big deal; null checks are short and cheap. – Eric Lippert Jun 03 '09 at 22:59
  • 6
    Incidentally, there are a number of places where the C# compiler DOES perform micro-optimizations like this if an expression is known to be non-null because it is the result of a new expression. This just is one that we missed. – Eric Lippert Jun 03 '09 at 23:04
  • 4
    I briefly discuss this optimization here: http://blogs.msdn.com/ericlippert/archive/2009/06/11/what-does-the-optimize-switch-do.aspx – Eric Lippert Jun 11 '09 at 15:18

5 Answers5

22

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
  • 19,439
  • 4
  • 63
  • 103
  • 2
    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 Jun 03 '09 at 20:31
  • 10
    If the constructor fails (before the try) though, the finally will not be executed. – Zifre Jun 03 '09 at 20:31
  • 11
    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 Jun 03 '09 at 20:35
  • @spoulson & @Zifre: You are right - try {} has to at least execute. But using() is designed for the general case. It might not be a new operator - as Max points out it might be a factory or some other method that could return null. if it fails, no try {}. If it succeeds or returns null, try will be executed. – n8wrl Jun 03 '09 at 20:39
  • 2
    @n8 - you should edit my answer into yours so that the OP can accept yours as a complete answer. – dss539 Jun 03 '09 at 21:30
  • 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 Jun 04 '09 at 06:32
  • That is incorrect. If you never enter the try, the finally will not be executed. – Robin Clowers Jun 30 '09 at 22:38
11

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

dss539
  • 6,804
  • 2
  • 34
  • 64
  • Why does that even compile? It is garanteed to be a runtime error. – Jonathan Allen Jun 03 '09 at 20:35
  • 2
    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 Jun 03 '09 at 20:37
  • 1
    And while it is unlikely you would code = null explictly, it could be = SomeFactoryMethod() which could return null. – n8wrl Jun 03 '09 at 20:41
  • 2
    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 Jun 03 '09 at 20:52
  • 2
    @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 Jun 03 '09 at 20:54
  • 2
    ...and it does not compile: error CS1656: Cannot assign to 'simp' because it is a 'using variable' – Fredrik Mörk Jun 03 '09 at 20:57
  • @Fredrik - thanks. so the compiler enforces some form of invariance on it, either out of necessity or kindness. – dss539 Jun 03 '09 at 21:22
  • 1
    It is also legal (compilable) to say: `using ((Simple)null) { }` – Jeppe Stig Nielsen Apr 29 '13 at 14:18
  • 1
    @MaxSchmeling "no reason for the using if you don't": side effects. The object's constructor and dispose method might have side effects, such as starting and stopping a remote service that is called inside the using block. Bad design, certainly, but a possible reason. – phoog Dec 21 '18 at 14:58
  • @phoog that's a good point. I also find myself relying on using blocks to acquire and free locks, for example. – dss539 Dec 21 '18 at 15:07
4

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
  • 4,655
  • 1
  • 30
  • 25
  • 1
    You would like to be able to change the `simp` variable within the using block?? – Tom Hawtin - tackline Jun 04 '09 at 01:30
  • I didn't realize that the compiler enforces simp as effectively readonly. – Dolphin Jun 05 '09 at 14:06
  • 3
    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 Jun 05 '09 at 17:49
2

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
  • 2,704
  • 3
  • 35
  • 54
0

The code must be translated this way to avoid possible NullReferenceException when disposing the object. As per C# language reference, the using statement accepts not only a local variable declaration as its first nonterminal resource_acquisition symbol, but any expression. Consider the following code:

DisposableType @object = null;
using(@object) {
    // whatever
}

Clearly, unless null-conditional @object?.Dispose() in the finnaly block, an exception would ensue. The null check is superfluous only when the expression is of a non-nullable value type (non-nullable struct). And indeed, according to the aforementioned language reference it is absent in such case.