好的,首先考虑以下简单的类实现。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestNamespace
{
class ILOrderTest
{
public int DeclarationTests()
{
int intDeclaredAtTop = 0;
for (int intDeclaredInForLoopDef = 0; intDeclaredInForLoopDef < 10; intDeclaredInForLoopDef++)
{
int intDeclaredInForLoopBody = intDeclaredInForLoopDef;
intDeclaredAtTop = intDeclaredInForLoopBody;
}
int intDeclaredAfterForLoop;
intDeclaredAfterForLoop = intDeclaredAtTop;
return intDeclaredAfterForLoop;
}
}
}
正如我们所看到的,许多变量在我们的方法的不同位置被声明,可以假设当C#解释器读取我们的文件时,它会以这样一种方式组织IL,即已声明的对象将在我们编写变量定义代码的位置进行定义。
然而,在编译和检查我们的IL之后,我们看到了一个非常不同的故事。
类ILOrderTest IL
.method public hidebysig
instance int32 DeclarationTests () cil managed
{
// Method begins at RVA 0x2050
// Code size 38 (0x26)
.maxstack 2
.locals init (
[0] int32 intDeclaredAtTop,
[1] int32 intDeclaredInForLoopDef,
[2] int32 intDeclaredInForLoopBody,
[3] int32 intDeclaredAfterForLoop,
[4] int32 CS$1$0000,
[5] bool CS$4$0001
)
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
IL_0003: ldc.i4.0
IL_0004: stloc.1
IL_0005: br.s IL_0011
// loop start (head: IL_0011)
IL_0007: nop
IL_0008: ldloc.1
IL_0009: stloc.2
IL_000a: ldloc.2
IL_000b: stloc.0
IL_000c: nop
IL_000d: ldloc.1
IL_000e: ldc.i4.1
IL_000f: add
IL_0010: stloc.1
IL_0011: ldloc.1
IL_0012: ldc.i4.s 10
IL_0014: clt
IL_0016: stloc.s CS$4$0001
IL_0018: ldloc.s CS$4$0001
IL_001a: brtrue.s IL_0007
// end loop
IL_001c: ldloc.0
IL_001d: stloc.3
IL_001e: ldloc.3
IL_001f: stloc.s CS$1$0000
IL_0021: br.s IL_0023
IL_0023: ldloc.s CS$1$0000
IL_0025: ret
} // end of method ILOrderTest::DeclarationTests
请注意,我们方法中的所有对象都已经被收集并在
.locals init...
调用中进行了初始化。如果我有时间,我可能会进一步研究为什么.Net IL是这样组织的,但如果我必须做出一个有根据的猜测,那就是变量声明涉及一定的开销,并且将特定范围内的所有变量捆绑在一起可能会节省一些CPU周期,而不是每次声明新对象时都进行多次调用
.locals init...
。
希望这能提供更进一步的明确性。Jon的答案按规范定义是正确的,但这可能会阐明为什么规范是以这种方式编写的。
s
,这不正常吗? - Soner Gönülforeach
后面使用与foreach
中相同的变量名?你从中获得了什么?在我看来,除了混淆它们之外,没有任何好处,这会成为一个错误的重要来源。如果以后想将其移动到方法的顶部,它也无法编译。我喜欢编译器有时指导我编写更好的代码(就健壮性、可维护性和可读性而言)。 - Tim Schmelter