Разлика между Try-Finally Dispose и Using-statement

Гмурках се в анализа на кода на 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-метод.

Казва метод за изхвърляне; защото какъв вид 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 в спецификацията на C#. - person usr; 30.07.2014
comment
Това обяснява много! Благодаря. Също @weston: Не знаех това, добре е да знам. Има смисъл, using се извиква Dispose; неизвършването на нулева проверка може да доведе до изключение, разбира се. - person Matthijs; 30.07.2014
comment
@Matthijs - И има нещо, от което IDisposable изпълнителите трябва да се пазят; вижте Клауза Using не успява да извика 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