C# using() statements
In C#.NET, the using keyword is used two different ways:
- using directive
- using statement
A using directive at the top of a .cs file imports or aliases a namespace or type throughout the .cs file. Several using directive examples are shown below.
// Import all types from namespace
// Types within the namespace can be accessed in code without specifying the
// namespace
using MyNamespace;
// Import static members of type
// Static members of the type can be accessed in code without specifying the
// namespace or type
using static MyNamespace.MyClassNameWithStaticMembers;
// Create namespace alias
// Types within the namespace can be accessed in code via the alias in addition to
// the fully qualified namespace
using mySubNamespaceAlias = MyNamespace.MySubNamespace;
// Create type alias
// The type can be accessed in code via the alias in addition to the fully qualified
// namespace and type
using myTypeAlias = MyNamespace.MyClassName;
This remainder of this post, however is not about using directives, but is instead about using statements.
The purpose of a using statement, sometimes referred to as a using block, is to ensure that the IDisposable.Dispose()
method gets called on an object when the code block associated with the using statement ends.
The general format of a using statement is shown below.
using( /* variable declaration | object reference */ )
{
// Do Something
}
There are a few things notable about at code snippet above:
- The "thing" inside of parenthesis can be either by an object reference, or a variable declaration with assignment.
- For both the variable declaration and object reference usages, the "thing" used within the parenthesis of the using statement must implement
IDisposable
. - You must use the curly braces!
In practice, the most common usage of the using statement is likely when it is used to declare and create a variable that is used inside of the block, then cleaned up and goes out of scope when the block ends.
using ( var myDisposableObject = new MyDisposableObject() )
{
myDisposableObject.DoSomething();
}
That code snippet is roughly equivalent to the following snippet, but there are some subtle differences.
var myDisposableObject = new MyDisposableObject();
try
{
myDisposableObject.DoSomething();
}
finally
{
myDisposableObject.Dispose();
}
Those snippets have similar behavior in that they both accomplish the following:
- Declare the variable
myDisposableObject
- Create a new instance of
MyDisposableObject
and assign it tomyDisposableObject
- Call
myDisposableObject.DoSomething()
- Call
myDisposableObject.Dispose()
, regardless of whethermyDisposableObject.DoSomething()
does or does not throw an exception
But, as stated before, there are some subtle differences:
- Scope of variable
myDisposableObject
- Whether
myDisposableObject
can be set to something else
The most notable difference has to do with the scope of the variable myDisposableObject
:
- In the
using(...){...}
example, the variable only exists within the scope of the using block itself, cannot be accessed after the using block as exited. - In the
try{...} finally{...}
example, the variable is defined within the scope outside of the try and finally blocks, and therefore continues to exist even after the finally block has exited.
The other difference has to do with whether the variable can be set to a different object.
- In the
using(...){...}
example, the variable is declared inside of the using statement itself, and that variable cannot be changed to null or to reference another object. For example, the compiler will flag and error onmyDisposableObject = null
within the curly braces of the using statement. - In the
try{...} finally{...}
example, the variable is declared outside of the using statement itself,and the variable can be changed to null or to reference another object. For example, the compiler will not flag an error onmyDisposableObject = null
within the curly braces of the try block.
One could write a using statement that is more similar to try..finally example by using an object reference within the using(...)
instead of using a variable declaration.
var myDisposableObject = new MyDisposableObject();
using (myDisposableObject )
{
}
In this case, the variable myDisposableObject
is declared outside of the using statement, similar to how it was declared outside of the try...finally block. Therefore, the variable can be set to null or to another reference within the curly braces of the using statement, and variable continues to exist after the using statement has exited.
This leads to an interesting question, however:
What happens if the object reference used within the parenthesis of the using block is null?
var myDisposableObject = null;
using (myDisposableObject )
{
}
That is allowed, and the executable code generated by the compiler/framework protects against that! In other words, the generated code is really something more like this:
var myDisposableObject = null;
try
{
}
finally
{
if (null != myDisposableObject)
{
myDisposableObject.Dipose();
}
}
Although there are some people that are adamant that passing object references into the using statement is "bad style" or "should be avoided at all costs", I personally find it very useful in some situations, especially in cleanup code within a Dispose()
method. Consider the following example:
public void Dispose()
{
using ( _myField )
{
_myField = null;
}
}
What the heck? Will
_myField.Dispose()
ever get called if the object reference is set to null within the curly braces of the using statement, or will it throw aNullReferenceException
?
Interestingly, if _myField
was non-null when it was referenced in the parenthesis of the using statement, then Dispose()
will still get called when the using block ends, even if _myField
is set to null within the curly braces! That means the framework is conceptually, automatically creating a new variable of type IDisposable
, assigning that variable to the object reference that was passed into the parenthesis, and using that variable to call Dispose()
, if necessary.
In essence, that means that that using statement always creates a new variable scoped to the using block. Sometimes it is explicitly declared by the programmer, and other times it is implicitly created and is not accessible to the programmer.
using (myObject)
{
}
...is just like...
using( var IDisposable disposableHiddenObject = myObject)
{
}
...if the disposableHiddenObject
is hidden to the programmer because it was not explicitly declared.
All of this behavior is very beneficial in cleanup code, especially within Dispose()
methods that might be called multiple times, or if specific fields may have already been disposed and/or set to null for other reasons. The reason it is so beneficial is because it avoids having to do extra null checks or explicitly call dispose! It allows you to blindly set the field to null, and still have it's Dispose()
method called.
This cleanup pattern becomes increasingly beneficial when you consider that using statements can be stacked (which will be discussed later in this article). For example, which would rather do?
public void Dispose()
{
using ( _myField1 )
using ( _myField2 )
using ( _myField3 )
{
_myField1 = null;
_myField2 = null;
_myField3 = null;
}
}
...or...
public void Dispose()
{
if ( null != _myField1 )
{
_myField1 .Dispose();
_myField1 = null;
}
if ( null != _myField2 )
{
_myField2 .Dispose();
_myField2 = null;
}
if ( null != _myField3 )
{
_myField3 .Dispose();
_myField3 = null;
}
}
Before getting into the stacking mentioned above, I wanted to clarify one thing: The object reference passed into the using statement has to either explicitly implement IDisposable
, implement IDisposable
by inheritence, or might be castable as IDisposable
when it is passed in.
That third option means you can do something that does not call Dispose()
now because the object does not yet implement 'IDisposable', but will automatically start calling Dispose()
once the object does start implementing 'IDisposable'.
public void Dispose()
{
// The object _myField might or might not actually implement IDisposable.
// If it does, then its Dispose() will get called. If it does not then no big
// deal because that just means null gets passed in, which is allowed. Either
// way, _myField will always get set to null.
using ( _myField as IDisposable )
{
_myField = null;
}
}
One of the Dispose()
examples above showed stacking of the using statement. As you saw in that example, the stacking can be very beneficial. But, there are a few things to keep in mind:
- Order in which
Dispose()
methods gets called - What happens if one of the
Dispose()
methods throws
As a programmer, when I see something like this...
// Stacked
using ( object1 )
using ( object2 )
using ( object3)
{
// Do Something
}
...my brain automatically thinks of that as nesting. After all, there are plenty of programming language choices in which curly braces are optional if there is only one statement. If others are like me, they would also think of them as nested.
// Nested
using ( object1 )
{
using ( object2 )
{
using ( object3)
{
// Do Something
}
}
}
But that nested snippet above is not true! One should really think of the using statements as being "stacked" instead of "nested".
If the blocks were truly nested, then one would expect Dispose()
to get called on the objects in the following order:
- object3.Dispose()
- object2.Dispose()
- object1.Dispose()
But, the reality is that they get called in the order written in the code:
- object1.Dispose()
- object2.Dispose()
- object3.Dispose()
The other reason to think of them as stacked instead of nested has to do with exceptions thrown by the Dipose()
methods themselves. Granted, everything you read says a Dispose()
should never, ever throw an exception or allow an exception to bubble out (and also that it should be allowed to be called any number of times). But, what happens if it does throw?
In the nested example, if object3.Dispose()
happens to throw an exception, then object2.Dispose()
will still get called, followed by object1.Dispose()
. In the stacked example, however, if object1.Dispose()
happens to throw an exception, then neither object2.Dispose()
nor object2.Dispose()
will get called!
Programmer, Engineer