Почему использование (ноль) допустимого регистра в C#?
Может ли кто-нибудь объяснить мне, почему приведенный ниже код действителен в C# и выполняет вызов Console.WriteLine
?
using (null)
{
Console.WriteLine ("something is here")
}
Он компилируется в (наконец, блок показан). Как видите, компилятор решает не выполнять Dispose()
метод и прыгает на endfinally
инструкция.
IL_0013: ldnull
IL_0014: ceq
IL_0016: stloc.1
IL_0017: ldloc.1
IL_0018: brtrue.s IL_0021 // branches here and decide not to execute Dispose()
IL_001a: ldnull
IL_001b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0020: nop
IL_0021: endfinally
Тем не менее, если я запускаю следующий код, он потерпит неудачу с NullReferenceException
(что ожидается):
((IDisposable)null).Dispose();
IL_0023: ldnull
IL_0024: callvirt instance void [mscorlib]System.IDisposable::Dispose()
Почему первая версия компилируется? Почему компилятор решает не выполнять Dispose()
? Есть ли другие случаи, когда компилятор может решить не вызывать Dispose()
в using
блок?
3 ответа
Спецификация языка в явном виде заявляет (8.13), что захваченное значение при необходимости проверяется на нулевое значение, т.е. finally
по существу (с оговорками вокруг ненулевых типов)
if(tmp != null) tmp.Dispose();
Я часто использую это в своих интересах, для вещей, которые могут быть нулевыми, но когда это не так: нужно избавляться. На самом деле, вот полезный сценарий (перечисление вручную IEnumerable
):
IEnumerable blah = ...; // note non-generic version
IEnumerator iter = blah.GetEnumerator();
using(iter as IDisposable)
{
// loop
}
как неуниверсальная версия IEnumerator
не обязательно IDisposable
, но когда это так, следует утилизировать.
Я думаю, что это естественный результат более общего случая using(some_expression)
, где some_expression
разрешено оцениватьnull
,
Потребовалось бы специальное правило, чтобы отличить этот случай от более общего.
Он будет игнорировать, если объект является нулевым - http://msdn.microsoft.com/en-us/library/yh598w02.aspx