5

I have been diving into Code Analysis of Microsoft and stumbled upon something quite interesting. .NET seems to use two different types of Dispose, depending on the way it is called. Take the following two options:

public void SqlConnectionUsing()
{
    using (SqlConnection connection = new SqlConnection())
    {
    }
}

public void SqlConnectionFinally()
{
    SqlConnection connection = new SqlConnection();
    try
    {
    }
    finally
    {
        connection.Dispose();
    }
}

Both options get translated to exactly the same thing; during compilation. The using becomes a try-finally statement with inside the finally statement a call to a Dispose-method.

I say a dispose-method; because what kind of dispose-method depends on the way you have written your code.

When going for the using-statement, a call goes to callvirt instance void [mscorlib]System.IDisposable::Dispose() (it is the exact IL-line).

And manually taking the try-finally option, the dispose-statement changes to: callvirt instance void [System]System.ComponentModel.Component::Dispose().

Why is there a difference in what dispose function gets called?

I can add the entire IL-code if required.

Matthijs
  • 3,162
  • 4
  • 25
  • 46

2 Answers2

4

During compilation, the using statement translates to:

try
{
}
finally
{
    ((IDisposable)connection).Dispose();
}

You can actually define two Dispose() methods inside the same class, one explicitly for the IDisposable interface, and a class method:

public class X : IDisposable
{
    void IDisposable.Dispose() { } 
    public void Dispose() { }
}

You could really ruin someone's day by letting these methods have different behavior, though.

Furthermore, you can create a Dispose() method in a class that does not implement IDisposable, but you won't be able to place it in a using statement.

C.Evenhuis
  • 25,996
  • 2
  • 58
  • 72
  • 2
    FYI `using` comes with a `null` check. – weston Jul 30 '14 at 10:31
  • 1
    This is 8.13 in the C# spec. – usr Jul 30 '14 at 10:31
  • That explains alot! Thanks. Also @weston: I did not know that, good to know. Does make sence, `using` calls Dispose itself; not doing a nullcheck could result in an exception ofcourse. – Matthijs Jul 30 '14 at 10:33
  • @Matthijs - And there's a gotcha for `IDisposable` implementers to beware of; see [Using clause fails to call Dispose?](http://stackoverflow.com/questions/11896282/using-clause-fails-to-call-dispose) – groverboy Jul 30 '14 at 12:12
  • Actually the behavior isn't quite equivalent to `((IDisposable)connection).Dispose()`, but rather `CallDispose(connection)`, assuming `void CallDispose(T it) where T:IDisposable { if (it != null) it.Dispose(); }`. If `it` is a struct with a (likely do-nothing) `IDisposable` implementation, the former will box it but the latter will not. Note that because C# disposes a copy of the argument to `using`, it's not possible to usefully employ a struct with a non-trivial `Dispose` method even in cases where doing so would otherwise improve efficiency. – supercat Aug 05 '14 at 21:56
0

That is because the using always uses the IDisposable.Dispose() and goes upward from there (so it is actually an interface method call).

It is actually:

using (IDisposable x = ...)
{ }

In the finally, you are actually calling the Component.Dispose() method, since that is the highest available Dispose method for SqlConnection.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325