Разница между оператором Try-Finally Dispose и оператором использования

Я погрузился в анализ кода Microsoft и наткнулся на кое-что довольно интересное. .NET, кажется, использует два разных типа Dispose, в зависимости от того, как он называется. Возьмите следующие два варианта:

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

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

Оба варианта переводятся в одно и то же; во время компиляции. Использование становится оператором try-finally с внутри оператора finally вызовом метода Dispose.

Я говорю метод удаления; потому что метод удаления зависит от того, как вы написали свой код.

При переходе на using-statement вызов идет на callvirt instance void [mscorlib]System.IDisposable::Dispose() (это точная IL-линия).

И вручную выбрав опцию try-finally, оператор dispose изменится на: callvirt instance void [System]System.ComponentModel.Component::Dispose().

Почему существует разница в том, какая функция dispose вызывается?

При необходимости могу добавить весь IL-код.


person Matthijs    schedule 30.07.2014    source источник


Ответы (2)


Во время компиляции оператор using преобразуется в:

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

На самом деле вы можете определить два метода Dispose() внутри одного и того же класса, один явно для интерфейса IDisposable и метод класса:

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

Однако вы действительно можете испортить чей-то день, позволив этим методам вести себя иначе.

Кроме того, вы можете создать метод Dispose() в классе, который не реализует IDisposable, но вы не сможете поместить его в оператор using.

person C.Evenhuis    schedule 30.07.2014
comment
FYI using поставляется с null чеком. - person weston; 30.07.2014
comment
Это 8.13 в спецификации С#. - person usr; 30.07.2014
comment
Это многое объясняет! Спасибо. Также @weston: я этого не знал, приятно знать. Имеет ли смысл, using вызывает Dispose себя; отсутствие проверки нуля может, конечно, привести к исключению. - person Matthijs; 30.07.2014
comment
@Matthijs - И есть кое-что, о чем следует остерегаться IDisposable разработчикам; см. Использование предложения не вызывает Dispose? - person groverboy; 30.07.2014
comment
На самом деле поведение не совсем эквивалентно ((IDisposable)connection).Dispose(), а скорее CallDispose(connection), предполагая void CallDispose<T>(T it) where T:IDisposable { if (it != null) it.Dispose(); }. Если it является структурой с (вероятно, ничего не делающей) реализацией IDisposable, первая упакует ее, а вторая нет. Обратите внимание, что поскольку C# размещает копию аргумента в using, невозможно с пользой использовать структуру с нетривиальным методом Dispose даже в тех случаях, когда в противном случае это повысило бы эффективность. - person supercat; 06.08.2014

Это потому, что using всегда использует IDisposable.Dispose() и идет вверх оттуда (поэтому на самом деле это вызов метода интерфейса).

Это на самом деле:

using (IDisposable x = ...)
{ }

Наконец, вы фактически вызываете метод Component.Dispose(), так как это самый высокий доступный метод Dispose для SqlConnection.

person Patrick Hofman    schedule 30.07.2014