Что произойдет, если я вернусь до окончания использования оператора?Будет ли называться распоряжение? - PullRequest
107 голосов
/ 14 июля 2010

У меня следующий код

using(MemoryStream ms = new MemoryStream())
{
     //code
     return 0;
}

Метод dispose() вызывается в конце using фигурных скобок } верно?Поскольку I return до конца оператора using, будет ли объект MemoryStream расположен правильно?Что здесь происходит?

Ответы [ 5 ]

158 голосов
/ 14 июля 2010

Да, Dispose будет вызван. Он вызывается, как только выполнение покидает область действия блока using, независимо от того, что означает выход из блока, будь то конец выполнения блока, оператор return или исключение.

Как правильно указывает @Noldorin, использование блока using в коде компилируется в try / finally, при этом Dispose вызывается в блоке finally. Например, следующий код:

using(MemoryStream ms = new MemoryStream())
{
     //code
     return 0;
}

фактически становится:

MemoryStream ms = new MemoryStream();
try
{
    // code
    return 0;
}
finally
{
    ms.Dispose();
}

Таким образом, поскольку finally гарантированно будет выполняться после того, как блок try завершит выполнение, независимо от его пути выполнения, Dispose гарантированно будет вызван, несмотря ни на что.

Дополнительные сведения см. В этой статье MSDN .

Добавление:
Просто небольшое предостережение: поскольку гарантированно вызывается Dispose, почти всегда хорошая идея, чтобы Dispose никогда не выдавал исключение при реализации IDisposable. К сожалению, в базовой библиотеке есть некоторые классы, которые do генерируют при определенных обстоятельствах, когда вызывается Dispose - я смотрю на вас, Справочник по услугам WCF / Клиентский прокси! - и когда это происходит, может быть очень трудно отследить исходное исключение, если Dispose было вызвано во время размотки стека исключений, поскольку исходное исключение проглатывается в пользу нового исключения, сгенерированного вызовом Dispose. Это может быть безумно расстраивающим. Или это разочаровывающе сводит с ума? Один из двух. Возможно оба.

17 голосов
/ 14 июля 2010
Операторы

using ведут себя точно так же, как блоки try ... finally, поэтому всегда будут выполняться на любых путях выхода кода. Тем не менее, я считаю, что они подвержены очень редким и редким ситуациям, в которых блоки finally не вызываются. Один пример, который я могу вспомнить, это если поток переднего плана завершается, когда фоновые потоки активны: все потоки, кроме GC, приостановлены, что означает, что finally блоки не запускаются.

Очевидное редактирование: они ведут себя одинаково, за исключением логики, которая позволяет им обрабатывать IDisposable объекты, д'О.

Бонусное содержание: их можно сложить (где типы различаются):

using (SqlConnection conn = new SqlConnection("string"))
using (SqlCommand comm = new SqlCommand("", conn))
{

}

А также через запятую (где типы одинаковы):

using (SqlCommand comm = new SqlCommand("", conn), 
       SqlCommand comm2 = new SqlCommand("", conn))
{

}
4 голосов
/ 14 июля 2010

Ваш объект MemoryStream будет расположен правильно, не нужно беспокоиться об этом.

3 голосов
/ 14 июля 2010

С помощью оператора using объект будет удален независимо от пути завершения.

Дальнейшее чтение ...

0 голосов
/ 14 июля 2010

Посмотрите на ваш код в отражателе после его компиляции.Вы обнаружите, что компилятор реорганизует код, чтобы гарантировать, что dispose вызывается в потоке.

...