为什么编译器添加一个不必要的局部变量
这个C#代码:
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();
}
}
}
产生这个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
}
为什么MSIL添加了flag1,继续执行相同的逻辑来设置标志,将flag1设置为标志,最后检查!flag1。 这对我来说似乎是一种编译器效率低下。
更新:我正在使用Telerik的JustDecompile,虽然结果与ILDASM完全不同,但额外的布尔值仍然以调试模式创建。
此外,我通过完全删除布尔值来修改代码,并且调试版本仍然添加了布尔值。 我真的在寻找为什么编译器会这样做。
它似乎是创建一个临时本地来保存比较结果(即一次性== null)。
尝试这个例子:
class Program
{
static void Main()
{
if (1 == 1) return;
}
}
..在我的盒子上生成以下IL(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
}
本地V_0被创建,即使它实际上没有被使用。 我相信这是一个明显的优化,即使对于'非'优化编译:) ..或更可能:生成所有必要的代码以启用调试。 我不知道如何在调试会话中使用它,但这是我最好的猜测。
编译优化 (即发布配置)时,我没有看到额外的本地。
当我编写了一个类似的程序时,额外的变量没有出现在发布模式中。 在汇编视图中相同,调试显示额外的变量被设置([ebp-44h])并且释放没有:
调试:
发布:
链接地址: http://www.djcxy.com/p/79855.html上一篇: Why Does The Compiler Add An Unnecessary Local Variable