Що станеться, якщо я повернусь до закінчення використання оператора? Чи буде називатися розпорядження?


115

У мене такий код

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

dispose()Метод викликається в кінці usingзаяви брекет }правильно? Оскільки я returnдо кінця usingвиписки, чи буде MemoryStreamоб’єкт розміщений належним чином? Що тут відбувається?


4
@JonH: Знайдіть точний дублікат, а потім проголосуйте, щоб закрити в цьому випадку, будь ласка.
Нолдорін

@Noldorin: Я пішов шукати дурня з цього приводу, тому що зрозумів, що його, мабуть , просили раніше, але я не зміг його знайти. Я здогадуюсь, там все ще є легкі питання. :)
Рандольфо

@JonH та @Noldorin - дублікати були б представлені, коли питання було сформовано, він шукає "подібні запитання", особливість, яку люди, здається, недостатньо використовує.
Адам Холдсворт

@Adam: спробуйте самі. Скопіюйте / вставте заголовок і подивіться, які дублікати отримує система. Я дам вам підказку: відповідь - ні. Діти, якщо ви здійснюєте пошук через пошук Google або SO. Здається, цього питання раніше не задавали.
Рандольфо

Аааа ... я беру це назад. Щойно я знайшов близький дублікат, після дуже присвяченого пошуку: stackoverflow.com/questions/2641692/… Тепер питання задається зовсім інакше, але остаточне питання майже те саме. Я гадаю, ми можемо вважати це дурпом врешті-решт.
Рандольфо

Відповіді:


167

Так, 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викликом. Це може бути шалено засмучує. Або це розчаровує розум? Одне з двох. Можливо і те й інше.


4
Я думаю, що ви знайдете, що це ефективно компілюється в блок "спроб нарешті" з викликом до, Disposeнарешті, тому це ефективно працює над реалізацією finally, як ви описуєте.
Нолдорін

@Noldorin: точно. Хоча я вважаю, що я міг би бути явним щодо цього. Редагувати майбутні ....
Randolpho

1
Також зауважте, що є деякі обставини, за яких остаточно не гарантується виконання блоку, наприклад використання Environment.FailFast та якщо відбувається StackOverFlowException.
Крістофер МакАкнейні

@ C.McAtackney: також хороший момент. Також IIRC, OutOfMemoryException; в основному, якщо ви не можете зловити виняток, оскільки це критичний збій виконання, Dispose не буде викликано. Звичайно, у такому випадку програма гарантує збій разом із будь-якою пам'яттю, що виділяється, тому в 99,9% випадків це не проблема, якщо ви не займаєтеся химерними речами, такими як запис у файл у розпорядженні методом розпорядження . Крім катастрофічного краху програми, тобто.
Рандольфо

Ніколи не слід використовувати оператор 'using ()' з WCF - докладнішу інформацію див. У цій статті . Ось фрагмент, який я використовую для проксі-серверів WCF: 'WCFProxy variaName = null; спробуйте {variableName = новий WCFProxy (); // Код TODO тут зміннийName.Proxy.Close (); змінноїName.Dispose (); } catch (Виняток) {if (variaName! = null && variableName.Proxy! = null) {variaName.Proxy.Abort (); } кинути; } '
Дейв Блек

18

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

Очевидна редакція: вони поводяться однаково, крім логіки, яка дозволяє їм обробляти об'єкти, що не використовуються, 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))
{

}

4

Ваш об’єкт MemoryStream буде розміщений належним чином, про це не потрібно хвилюватися.



0

Подивіться на свій код у відбивачі після його складання. Ви побачите, що компілятор відновлює код для забезпечення виклику dispose у потоці.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.