Using statement vs. IDisposable.Dispose()

37,404

Solution 1

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.

Solution 2

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 !

Solution 3

//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.

Solution 4

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:

  1. 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`.
  2. 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 Disposed 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.

Solution 5

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();
}
Share:
37,404
oscilatingcretin
Author by

oscilatingcretin

pronouns: you/your

Updated on February 10, 2020

Comments

  • oscilatingcretin
    oscilatingcretin over 4 years

    It has been my understanding that the using statement in .NET calls an IDisposable object's Dispose() method once the code exits the block.

    Does the using statement do anything else? If not, it would seem that the following two code samples achieve the exact same thing:

    Using Con as New Connection()
        Con.Open()
        'do whatever '
    End Using
    
    Dim Con as New Connection()
    Con.Open()
    'do whatever '
    Con.Dispose()
    

    I will give the best answer to whoever confirms that I am correct or points out that I am wrong and explains why. Keep in mind that I am aware that certain classes can do different things in their Dispose() methods. This question is about whether or not the using statement achieves the exact same result as calling an object's Dispose() method.