Что произойдет, если я вернусь до конца использования инструкции? Будет ли вызов вызываться?

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

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

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

Ответ 1

Да, 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. К сожалению, в основной библиотеке есть некоторые классы, которые вызывают определенные обстоятельства при вызове Dispose - я смотрю на вас, справочник службы WCF/клиентский прокси! - и когда это произойдет, может быть очень сложно отследить исходное исключение, если вызов Dispose был вызван во время стека исключений, поскольку исходное исключение проглатывается в пользу нового исключения, генерируемого вызовом Dispose. Это может быть безумно расстраивающим. Или это расстраивает безумие? Один из двух. Возможно оба.

Ответ 2

Операторы

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

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

Бонусный контент: они могут быть сложены (где разные типы):

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

}

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

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

}

Ответ 3

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

Ответ 5

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