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

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

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

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


person NLV    schedule 14.07.2010    source источник
comment
Аааа ... беру это обратно. Я только что нашел почти дубликат после очень внимательного поиска: stackoverflow.com/questions/2641692/ Теперь вопрос задается совершенно по-другому, но конечный вопрос почти такой же. Полагаю, мы все-таки можем считать это обманом.   -  person Randolpho    schedule 14.07.2010


Ответы (5)


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

Как правильно указывает @Noldorin, использование блока using в коде компилируется в _5 _ / _ 6_, причем 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. Это может раздражать до безумия. Или это ужасно сводит с ума? Один из двух. Возможно оба.

person Randolpho    schedule 14.07.2010
comment
Я думаю, вы обнаружите, что он эффективно скомпилирован в блок try-finally с вызовом Dispose in finally, так что он эффективно отрабатывает реализацию finally, как вы описываете. - person Noldorin; 14.07.2010
comment
@Noldorin: точно. Хотя, полагаю, я мог бы сказать об этом прямо. Редактировать в ближайшее время .... - person Randolpho; 14.07.2010
comment
Также обратите внимание, что есть некоторые обстоятельства, при которых выполнение блока finally не гарантируется, например, при использовании Environment.FailFast и возникновении исключения StackOverFlowException. - person Christopher McAtackney; 14.07.2010
comment
@ C.McAtackney: тоже хороший момент. Также IIRC, OutOfMemoryException; в основном, если вы не можете поймать исключение из-за критического сбоя выполнения, Dispose не будет вызываться. Конечно, в таком случае программа гарантированно выйдет из строя вместе с любой выделенной ей памятью, поэтому в 99,9% случаев это не проблема, если только вы не делаете нестандартные вещи, такие как запись в файл в своем методе удаления. . Если не считать катастрофического сбоя программы. - person Randolpho; 14.07.2010
comment
Никогда не используйте оператор using () с WCF - см. эту статью для получения дополнительной информации. Вот фрагмент, который я использую для прокси WCF: 'WCFProxy variableName = null; попробуйте {имя_переменной = новый WCFProxy (); // Код TODO здесь variableName.Proxy.Close (); имя_переменной.Dispose (); } catch (исключение) {если (имя_переменной! = null && имя_переменной.Proxy! = null) {имя_переменной.Proxy.Abort (); } бросать; } ' - person Dave Black; 06.04.2011

Операторы 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), 
       comm2 = new SqlCommand("", conn))
{

}
person Adam Houldsworth    schedule 14.07.2010
comment
Согласно другому ответу в stackoverflow stackoverflow.com/a/9396151/6142097, для второго примера (с разделителями-запятыми), тип второго параметра указывать не нужно. - person mihkov; 24.11.2020
comment
@mihkov Хороший вопрос, только что попробовал сам. Я не возвращался к предыдущим языковым версиям, поэтому я не могу сказать, всегда ли это было так, или это небольшое изменение с Roslyn. - person Adam Houldsworth; 25.11.2020

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

person Otávio Décio    schedule 14.07.2010

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

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

person RSolberg    schedule 14.07.2010

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

person Wil P    schedule 14.07.2010