Dela via


What’s the deal with the C# “using” construct?

We spent a little bit of time today talking about the “using” construct and potential problems and confusion that people have been having with it.  For those who don’t know, the “using” statement is a simple C# construct that provides concise syntax for a very common pattern.  Specifically, the C# language specification says that it will convert code with the following structure:

 using ( expression )
    statement

into

 ExpressionType temp = expression;
try {
    statement
} finally {
    if (temp != null) {
       ((IDisposable)temp).Dispose();
    }
}

This allows you to concisely work with a resource that needs to be disposed in a timely fashion.  A good example of this are things like file handles.  There are a limited number that the OS can give out, and it’s quite possible to run out of them if you haphazardly request them and then depend on the garbage collector to clean them up for you.  Now, in a perfect world, the GC would be aware of non-memory resources and it would reclaim things like handles when they were no longer being used by you.  However, in today’s world that’s not how things work so the “using” construct can be used in conjunction with the IDisposable interface pattern to accomplish that.

Now, we could have not introduced the “using” construct, however without it code tends to get incredibly messy.  Consider if you are using two disposable resources, the code then becomes:

 ExpressionType1 temp1 = expression1;
try {
    ExpressionType temp2 = expression2;
    try {
        statement
    } finally {
        if (temp2 != null) {
            ((IDisposable)temp2).Dispose();
        }
    }
} finally {
    if (temp1 != null) {
        ((IDisposable)temp1).Dispose();
    }
}

Bleagh!  That’s pretty awful.  With “using” you can write that as:

 using (ExpressionType1 name1 = expression1, name2 = expression2)
    statement

or, alternatively

 using (expression1)
using (expression2)
    statement

(personally, I prefer the latter).  That’s a heck of a lot more concise and far easier to grok.

So, what’s the problem that people have been having with the “using” construct?  Well, as you may have already noticed there’s a potential problem whereby a resource you’ve acquired is not released in a timely manner.  When can that happen?  Well consider the following:

 ExpressionType temp = expression;
//asynchronous exception (like ThreadAbortException) is thrown now
try {
    statement
} finally {
    if (temp != null) {
        ((IDisposable)temp).Dispose();
    }
}

Uh-oh.  We have a problem.  You’ve acquired the resource in “expression”, but before entering the “try” some asynchronous exception occurs and so the resource is never disposed.  At first glance it seems like there is a trivial fix for this.  Simply change the code transformation so that it generates:

 ExpressionType temp = null;
try {
    temp = expression;
    statement
} finally {
    if (temp != null) {
        ((IDisposable)temp).Dispose();
    }
}

Terrific right?  Now if an asynchronous exception occurs after “expression” is invoked, we’ll be inside the “try” and so the “finally” block will execute and clean up the resource for us.   Unfortunately, that’s still not the case!  How so?  Well consider the following:

 ExpressionType temp = null;
try {
    temp = /*async-exception thrown before assignment occurs*/ expression;
    statement
} finally {
    if (temp != null) {
        ((IDisposable)temp).Dispose();
    }
}

Sigh…  The resource has been acquired, but when the “finally” runs “temp” will still be null, and nothing will get disposed at that time.

OMG!  The sky is falling!  The sky is falling!

Seems like you’re in a horrible position here right!?  How on earth will you know if your resources are going to be cleaned up?  Are we now in a world where you’re going to be leaking resources and you’re not going to be able to trust your managed code?

Fortunately, no, things are not as dire as they seem.  Why?  Well, first let’s start with an assumption.  If you are writing code that follows the .Net guidelines, and you listen to tools like FxCop, then you’re going to be writing your classes that implement IDisposable as recommended in the MSDN documentation.

What this means is that even if you forget, or fail (because of an asynchronous exception), to explicitly call “Dispose” on the resource it will be ok because the GC will eventually finalize your object which will end up doing the same. 

But wait a minute.  Didn’t I just say that depending on the GC to clean up critical resources was a bad practice since you might run out of resources before the GC kicks in?  Yes I did.  However, the difference here is that if you have the bad luck to run into this asynchronous exception problem you stand the chance of not disposing at most a couple of disposable objects.  And the chance of those few objects causing you to be unable to acquire any more resources is exceedingly slim.  Contrast that with code that just does something like this:

 while (true) {
    AcquireResource();
}

In this case you’re going to be leaking tons and tons of resources and it will very quickly become a problem for you.  In the asynchronous case you will be leaking a worst a couple of resources, and they will almost certainly be reclaimed before they could ever be a problem.

It should also be noted that these asynchronous exceptions occur in extremely bad situations (like a thread dying).  And, in native code, it’s often extremely difficult to recover from this at all.  The managed world allows you to have a good balance.  When an error occurs you may leak a resource, but even so it will quickly be reclaimed soon afterwards. 

So if you’ve ever seen the transformation done by the C# compiler and wondered why it did what it did, well now you know. 

Any thoughts or questions on this stuff?

Comments