2011-11-03 09:34:28 UTC
Как известно, using
в C# это try/finaly
с вызовом Dispose()
в блоке finally
, но компилятор для этой конструкции генерирует не самый оптимальный код для подавляющего большинства случаев.
Имеем использование объекта IDisposable
, с using
и без него:
static void Using() { using (MemoryStream ms = new MemoryStream()) { ms.Flush(); } } static void NoUsing() { MemoryStream ms = new MemoryStream(); try { ms.Flush(); } finally { ms.Dispose(); } }
Код для обоих генерируется одинаковый, за исключением одного ньюанса.
Код с using
:
.method private hidebysig static void Using() cil managed { .maxstack 1 .locals init ( [0] class [mscorlib]System.IO.MemoryStream ms) L_0000: newobj instance void [mscorlib]System.IO.MemoryStream::.ctor() L_0005: stloc.0 L_0006: ldloc.0 L_0007: callvirt instance void [mscorlib]System.IO.Stream::Flush() L_000c: leave.s L_0018 L_000e: ldloc.0 L_000f: brfalse.s L_0017 L_0011: ldloc.0 L_0012: callvirt instance void [mscorlib]System.IDisposable::Dispose() L_0017: endfinally L_0018: ret .try L_0006 to L_000e finally handler L_000e to L_0018 }
И «самописный» using
:
.method private hidebysig static void NoUsing() cil managed { .maxstack 1 .locals init ( [0] class [mscorlib]System.IO.MemoryStream ms) L_0000: newobj instance void [mscorlib]System.IO.MemoryStream::.ctor() L_0005: stloc.0 L_0006: ldloc.0 L_0007: callvirt instance void [mscorlib]System.IO.Stream::Flush() L_000c: leave.s L_0015 L_000e: ldloc.0 L_000f: callvirt instance void [mscorlib]System.IO.Stream::Dispose() L_0014: endfinally L_0015: ret .try L_0006 to L_000e finally handler L_000e to L_0015 }
Обратите внимание на инструкцию brfalse.s L_0017
в блоке finally
для первого варианта, — по сути она лишняя, т.к. инициализация объекта находится за пределами try
и если инициализация будет неуспешной, то будет выброшено исключение. Если же объект проинициализируется, то он гарантированно не будет null
, поэтому проверка объекта на null
по сути лишняя.
Если же переписать наш «самописный» using
c проверкой на null
в finally
, будет сгенерирован аналогичный код, как при использовании using
:
static void NoUsing() { MemoryStream ms = new MemoryStream(); try { ms.Flush(); } finally { if ( ms != null ) { ms.Dispose(); } } }
Результат:
.method private hidebysig static void NoUsing() cil managed { .maxstack 1 .locals init ( [0] class [mscorlib]System.IO.MemoryStream ms) L_0000: newobj instance void [mscorlib]System.IO.MemoryStream::.ctor() L_0005: stloc.0 L_0006: ldloc.0 L_0007: callvirt instance void [mscorlib]System.IO.Stream::Flush() L_000c: leave.s L_0018 L_000e: ldloc.0 L_000f: brfalse.s L_0017 L_0011: ldloc.0 L_0012: callvirt instance void [mscorlib]System.IO.Stream::Dispose() L_0017: endfinally L_0018: ret .try L_0006 to L_000e finally handler L_000e to L_0018 }