using ((IDisposable)null)
{
}
// Исключения NullReferenceException не будет.
// Метод Dispose вызван не будет.
Рассмотрим довольно распространенную конструкцию: Допустим, есть класс Some, реализующий интерфейс IDisposable:
class Some : IDisposable
{
public void Dispose()
{
Console.WriteLine("dispose");
}
}
и статический метод (например, это может быть какой-нибудь builder), возвращающий экземпляр Some:
static Some GetSome()
{
// Так получилось, что вернули null
return null;
}
Вопрос: "Возникнет ли NullReferenceException исключение, когда мы выйдем за пределы блока using?".:
using (Some some = GetSome())
{
if (some != null)
{
// Действия с some...
}
}// IDisposable.Dispose()
Ответ: "Нет, метод Dispose объекта some в данной ситуации вызван не будет.".
На самом деле, блок using будет оттранслирован в блок try...finally с проверкой на null:
Some some = GetSome();
try
{
if (some != null)
{
// Действия с some...
}
}
finally
{
if (some != null)
((IDisposable)some).Dispose();
}
Или:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 39 (0x27)
.maxstack 2
.locals init ([0] class NullUsing.Some some,
[1] bool CS$4$0000)
IL_0000: nop
IL_0001: call class NullUsing.Some NullUsing.Program::GetSome()
IL_0006: stloc.0
.try
{
IL_0007: nop
IL_0008: ldloc.0
IL_0009: ldnull
IL_000a: ceq
IL_000c: stloc.1
IL_000d: ldloc.1
IL_000e: brtrue.s IL_0012
IL_0010: nop
IL_0011: nop
IL_0012: nop
IL_0013: leave.s IL_0025
} // end .try
finally
{
IL_0015: ldloc.0
IL_0016: ldnull
IL_0017: ceq
IL_0019: stloc.1
IL_001a: ldloc.1
IL_001b: brtrue.s IL_0024
IL_001d: ldloc.0
IL_001e: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0023: nop
IL_0024: endfinally
} // end handler
IL_0025: nop
IL_0026: ret
} // end of method Program::Main
Ссылки:
Немного эффективнее транслировать в такой код:
ОтветитьУдалитьSome some = GetSome();
if some != null {
try {
// Действия с some...
}
finally {
some.Dispose();
}
}
Ведь изменить some вроде как нельзя.
Проверки на null в блоке try нет, и в try мы словим исключение NullReferenceException, поэтому в try мы сами должны вручную проверять на null. А в finally она есть.
ОтветитьУдалитьЕсть еще фишка с throw null :) вот она как раз бросает NullReferenceException
ОтветитьУдалитьДа вы правы, я не заметил сразу что проверку на null нужно делать самому. Правильно что сделали именно так. Код внутри using может быть разным и делать что то полезное если вернули null. К тому же похоже не запрещено изменять переменную внутри using блока.
ОтветитьУдалитьЕдинственно, что было бы полезно - это добавить присвоение null внутри finally после вызова Dispose.
> nesteruk
ОтветитьУдалитьНе совсем понял фишки с throw null; Насколько я понимаю эту конструкцию, работает не throw new NullReferenceException(); а скорее NullReferenceException кидается по причине непоняток, какое исключение генерировать (null).
@sunexdev
ОтветитьУдалитьWhy does C# allow you to "throw null"?
http://stackoverflow.com/questions/2195764/why-does-c-allow-you-to-throw-null
Самое интересное, что в MSDN про эту проверку на null не сказано ни слова. Хотя это и без того очевидно.
ОтветитьУдалитьhttp://msdn.microsoft.com/en-us/library/yh598w02.aspx
По-моему то что using не бросает исключение - вполне логично и даже удобно, так как в этом случае можно писать чего-нибудь наподобие:
ОтветитьУдалитьusing (someObject as IDisposable)
{
}
И не бояться исключения.
@dev-pit
ОтветитьУдалитьДа, логично, но не очевидно, поэтому и пришлось в этом убедиться.