Why Does The Compiler Add An Unnecessary Local Variable
This C# code:
private void LoadAssignments(AssignmentType assignmentType, Collection<Assignment> assignments)
{
bool flag;
DataTable lessons = this.GetResults(assignmentType);
try
{
IEnumerator enumerator = lessons.Rows.GetEnumerator();
try
{
while (true)
{
flag = enumerator.MoveNext();
if (!flag)
{
break;
}
DataRow row = (DataRow)enumerator.Current;
}
}
finally
{
IDisposable disposable = enumerator as IDisposable;
flag = disposable == null;
if (!flag)
{
disposable.Dispose();
}
}
}
finally
{
flag = lessons == null;
if (!flag)
{
lessons.Dispose();
}
}
}
produces this CIL (.NET 4)
.method private hidebysig
instance void LoadAssignments (
valuetype TTReporterCore.AssignmentType assignmentType,
class [mscorlib]System.Collections.ObjectModel.Collection`1<valuetype TTReporterCore.Assignment> assignments
) cil managed
{
.locals init (
[0] bool flag,
[1] class [System.Data]System.Data.DataTable lessons,
[2] class [mscorlib]System.Collections.IEnumerator enumerator,
[3] class [System.Data]System.Data.DataRow row,
[4] class [mscorlib]System.IDisposable disposable,
[5] bool flag1
)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldarg.1
IL_0003: call instance class [System.Data]System.Data.DataTable TTReporterCore.TTReader::GetResults(valuetype TTReporterCore.AssignmentType)
IL_0008: stloc.1
.try
{
IL_0009: nop
IL_000a: ldloc.1
IL_000b: callvirt instance class [System.Data]System.Data.DataRowCollection [System.Data]System.Data.DataTable::get_Rows()
IL_0010: callvirt instance class [mscorlib]System.Collections.IEnumerator [System.Data]System.Data.InternalDataCollectionBase::GetEnumerator()
IL_0015: stloc.2
.try
{
IL_0016: nop
IL_0017: br.s IL_0038
.loop
{
IL_0019: nop
IL_001a: ldloc.2
IL_001b: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0020: stloc.0
IL_0021: ldloc.0
IL_0022: stloc.s flag1
IL_0024: ldloc.s flag1
IL_0026: brtrue.s IL_002b
IL_0028: nop
IL_0029: br.s IL_003d
IL_002b: ldloc.2
IL_002c: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
IL_0031: castclass [System.Data]System.Data.DataRow
IL_0036: stloc.3
IL_0037: nop
IL_0038: ldc.i4.1
IL_0039: stloc.s flag1
IL_003b: br.s IL_0019
}
IL_003d: nop
IL_003e: leave.s IL_0062
}
finally
{
IL_0040: nop
IL_0041: ldloc.2
IL_0042: isinst [mscorlib]System.IDisposable
IL_0047: stloc.s disposable
IL_0049: ldloc.s disposable
IL_004b: ldnull
IL_004c: ceq
IL_004e: stloc.0
IL_004f: ldloc.0
IL_0050: stloc.s flag1
IL_0052: ldloc.s flag1
IL_0054: brtrue.s IL_0060
IL_0056: nop
IL_0057: ldloc.s disposable
IL_0059: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_005e: nop
IL_005f: nop
IL_0060: nop
IL_0061: endfinally
}
IL_0062: nop
IL_0063: nop
IL_0064: leave.s IL_007e
}
finally
{
IL_0066: nop
IL_0067: ldloc.1
IL_0068: ldnull
IL_0069: ceq
IL_006b: stloc.0
IL_006c: ldloc.0
IL_006d: stloc.s flag1
IL_006f: ldloc.s flag1
IL_0071: brtrue.s IL_007c
IL_0073: nop
IL_0074: ldloc.1
IL_0075: callvirt instance void [System]System.ComponentModel.MarshalByValueComponent::Dispose()
IL_007a: nop
IL_007b: nop
IL_007c: nop
IL_007d: endfinally
}
IL_007e: nop
IL_007f: ret
}
Why does the MSIL add the flag1, continue to perform the same logic to set flag, set flag1 to flag and finally, check for !flag1. This seems like a compiler inefficiency to me.
UPDATE: I was using Telerik's JustDecompile and while the results are quite different from ILDASM, the additional boolean is still created in debug mode.
Also, I modified the code by removing the boolean completely and the debug version still adds a boolean. I'm really looking for why the compiler does this.
It seems like a temporary local is created to hold the results of the comparison (ie disposable == null).
Trying this example:
class Program
{
static void Main()
{
if (1 == 1) return;
}
}
..is producing the following IL on my box (Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1):
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 1
.locals init (bool V_0)
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
IL_0003: br.s IL_0005
IL_0005: ret
}
local V_0 is created, even though it's actually not used. I believe it's an obvious optimisation even for 'non' optimized compilation :) ..or more likely: all necessary code is generated to enable debugging. I don't know how it might be used in a debugging session, but it's my best guess.
When compiled optimized (ie Release configuration) I don't see the extra local.
When I wrote a similar program, the extra variable did not appear in release mode. Same in assembly view, debugging showed the extra variable being set ([ebp-44h]) and release did not:
Debug:
Release:
链接地址: http://www.djcxy.com/p/79856.html上一篇: 与查找堆叠
下一篇: 为什么编译器添加一个不必要的局部变量