Using statement vs. IDisposable.Dispose()
using
is basically the equivalent of:
try
{
// code
}
finally
{
obj.Dispose();
}
So it also has the benefit of calling Dispose()
even if an unhandled exception is thrown in the code within the block.
As Brian Warshaw stated in here it's simply an implementation of try
and finally
block to make sure object is disposed. Adding to his answer, using
block also makes sure that the object is disposed even if you return inside using scope.
I was once curious about this myself and tested it out using the following approach:
Custom IDisposable test class and Main
private class DisposableTest : IDisposable
{
public string Name { get; set; }
public void Dispose() { Console.WriteLine("{0}.Dispose() is called !", Name); }
}
public static void Main(string[] args)
{
try
{
UsingReturnTest();
UsingExceptionTest();
}
catch { }
try
{
DisposeReturnTest();
DisposeExceptionTest();
}
catch { }
DisposeExtraTest();
Console.ReadLine();
}
Test cases implementation
private static string UsingReturnTest()
{
using (DisposableTest usingReturn = new DisposableTest() { Name = "UsingReturn" })
{
return usingReturn.Name;
}
}
private static void UsingExceptionTest()
{
using (DisposableTest usingException = new DisposableTest() { Name = "UsingException" })
{
int x = int.Parse("NaN");
}
}
private static string DisposeReturnTest()
{
DisposableTest disposeReturn = new DisposableTest() { Name = "DisposeReturn" };
return disposeReturn.Name;
disposeReturn.Dispose(); // # IDE Warning; Unreachable code detected
}
private static void DisposeExceptionTest()
{
DisposableTest disposeException = new DisposableTest() { Name = "DisposeException" };
int x = int.Parse("NaN");
disposeException.Dispose();
}
private static void DisposeExtraTest()
{
DisposableTest disposeExtra = null;
try
{
disposeExtra = new DisposableTest() { Name = "DisposeExtra" };
return;
}
catch { }
finally
{
if (disposeExtra != null) { disposeExtra.Dispose(); }
}
}
And the output is:
- UsingReturn.Dispose() is called !
- UsingException.Dispose() is called !
- DisposeExtra.Dispose() is called !
//preceeding code
using (con = new Connection()) {
con.Open()
//do whatever
}
//following code
is equivalent to the following (note the limited scope for con):
//preceeding code
{
var con = new Connection();
try {
con.Open()
//do whatever
} finally {
if (con != null) con.Dispose();
}
}
//following code
This is described here: http://msdn.microsoft.com/en-us/library/yh598w02.aspx
The using statement ensures that Dispose is called even if an exception occurs while you are calling methods on the object. You can achieve the same result by putting the object inside a try block and then calling Dispose in a finally block; in fact, this is how the using statement is translated by the compiler.
A using
statement is clearer and more concise than a try...finally{Dispose()}
construct, and should be used in nearly all cases where one does not want to allow a block to exit without Dispose
being called. The only common situations where "manual" disposal would be better would be when:
- A method calls a factory method which returns something that may or may not implement `IDisposable`, but which should be `Dispose`d if it does (a scenario which occurs with non-generic `IEnumerable.GetEnumerator()`). Well-designed factory interfaces should either return a type that implements `IDisposable` (perhaps with a do-nothing implementation, as is typically the case of `IEnumerator`) or else specify callers are not expected to `Dispose` the returned object. Unfortunately, some interfaces like non-generic `IEnumerable` satisfy neither criterion. Note that one cannot very well use `using` in such cases, since it only works with storage locations whose declared type implements `IDisposable`.
- The `IDisposable` object is expected to live even after the block is exited (as is often the case when setting an `IDisposable` field, or returning an `IDisposable` from a factory method).
Note that when returning an IDisposable
from a factory method, one should use something like the following:
bool ok = false; DisposableClass myThing; try { myThing = new DisposableClass(); ... ok = true; return myThing; } finally { if (!ok) { if (myThing != null) myThing.Dispose(); } }
to ensure that myThing
will get Dispose
d if it doesn't get returned. I wish there was a way to employ using
along with some "cancel Dispose" method, but no such thing exists.
The difference between the two is that, if an Exception is thrown in
Con.Open()
'do whatever
Con.Dispose
will not be called.
I'm not up on VB syntax, but in C#, the equivalent code would be
try
{
con = new Connection();
// Do whatever
}
finally
{
if (con != null) con.Dispose();
}