Есть ли ситуация, в которой Dispose не будет вызываться для блока "using"?
Это был вопрос телефонного интервью, который у меня был: есть ли время, когда Dispose не будет вызываться на объект, область действия которого объявлена блоком using?
мой ответ был нет - даже если исключение произойдет во время использования блока, Dispose все равно будет вызван.
интервьюер не согласился и сказал, что если using завернут в try -catch блок затем Dispose не будет вызван к моменту ввода блока catch.
Это идет вразрез с моим понимание конструкции, и я не смог найти ничего, что подтверждает точку зрения интервьюеров. Он прав или я неправильно понял вопрос?
8 ответов:
void Main() { try { using(var d = new MyDisposable()) { throw new Exception("Hello"); } } catch { "Exception caught.".Dump(); } } class MyDisposable : IDisposable { public void Dispose() { "Disposed".Dump(); } }Это :
Disposed Exception caught
четыре вещи, которые заставят Dispose не вызываться в блоке using:
- сбой питания на вашей машине, когда внутри блока using.
- ваша машина расплавляется атомной бомбой, находясь внутри блока использования.
- исключения неуловимым как
StackOverflowException,AccessViolationExceptionи возможно, другие.- окружающая среда.Содержащие
странно я читал об обстоятельствах, когда Dispose не будет вызван в блок использования только сегодня утром. Проверьте это блог на MSDN. Это связано с использованием Dispose с IEnumerable и ключевым словом yield, когда вы не повторяете всю коллекцию.
к сожалению, это не касается случая исключения, честно говоря, я не уверен в этом. Я бы ожидал, что это будет сделано, но, возможно, стоит проверить с помощью быстрого кода?
другие ответы о сбое питания,
Environment.FailFast()итераторы или обмана со стороныusingчто этоnull- все интересные. Но мне любопытно, что никто не упомянул, что я думаю, что это самая распространенная ситуация, когдаDispose()не будет называться даже в присутствииusing: когда выражение внутриusingвыдает исключение.конечно, это логично: выражение
usingбросил исключение, так что назначение не состоялось и есть ничего, что мы могли бы назватьDispose()on. Но одноразовый объект уже может существовать,хотя он может быть наполовину инициализирован. И даже в этом состоянии он уже может содержать некоторые неуправляемые ресурсы. Это еще одна причина, почему важно правильно реализовать одноразовый шаблон.пример проблемного кода:
using (var f = new Foo()) { // something } … class Foo : IDisposable { UnmanagedResource m_resource; public Foo() { // obtain m_resource throw new Exception(); } public void Dispose() { // release m_resource } }здесь, похоже
Fooвыпускаетm_resourceправильно, и мы используемusingправильно тоже. Но этоDispose()onFoois никогда не звонил, из-за исключения. Исправление в этом случае заключается в использовании финализатора и освобождении ресурса там тоже.
The
usingблок превращается компилятором вtry/finallyблок сам по себе,внутри существующейtryзаблокировать.например:
try { using (MemoryStream ms = new MemoryStream()) throw new Exception(); } catch (Exception) { throw; }становится
.try { IL_0000: newobj instance void [mscorlib]System.IO.MemoryStream::.ctor() IL_0005: stloc.0 .try { IL_0006: newobj instance void [mscorlib]System.Exception::.ctor() IL_000b: throw } // end .try finally { IL_000c: ldloc.0 IL_000d: brfalse.s IL_0015 IL_000f: ldloc.0 IL_0010: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0015: endfinally } // end handler } // end .try catch [mscorlib]System.Exception { IL_0016: pop IL_0017: rethrow } // end handlerкомпилятор не будет переставлять вещи. Так и происходит вот так:
- исключение выбрасывается или распространяется на
usingблокаtryчасть- управление оставляет
usingблокаtryчасть, и входит егоfinallyчасть
Да есть случай, когда dispose не будет вызван... вы слишком много думаете об этом. Случай, когда переменная в использующем блоке
nullclass foo { public static IDisposable factory() { return null; } } using (var disp = foo.factory()) { //do some stuff }не будет выдавать исключение, но если бы dispose был вызван в каждом случае. Однако конкретный случай, о котором упоминал ваш интервьюер, ошибочен.
интервьюер частично прав.
Disposeне может правильно очистить базовый объект на индивидуальной основе.вот статья из MSDN о том, как избегайте проблем с использованием блока С WCF. Вот это официальный обходной путь Microsoft, хотя я сейчас думаю, что a сочетание этого ответа и этот самый элегантный подход.