可能有人认为相比于 ForTest1,ForTest2 存储了数组的 Length,少了对于数组属性的频繁调用,会有更好的性能表现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
   | using System;
  namespace JITPropertyAccessInFor {     internal static class Program     {         public static void Main(string[] args)         {         }
          private static void Test1()         {             var a = new int[5];         }
          private static void ForTest1()         {             var a = new int[5];             for (var i = 0; i < a.Length; i++)             {                 Console.WriteLine(a[i]);             }         }
          private static void ForTest2()         {             var a = new int[5];             int len = a.Length;             for (var i = 0; i < len; i++)             {                 Console.WriteLine(a[i]);             }         }     } }
   | 
 
以下 是 上段代码编译出的 IL code:(以下所述栈均为操作数栈 (Operand stack))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   | .method private hidebysig static void  ForTest1() cil managed {      .maxstack  2    .locals init ([0] int32[] a, // 变量声明 (局部变量表)            [1] int32 i)   IL_0000:  ldc.i4.5    IL_0001:  newarr     [mscorlib]System.Int32    IL_0006:  stloc.0    IL_0007:  ldc.i4.0    IL_0008:  stloc.1    IL_0009:  br.s       IL_0017    IL_000b:  ldloc.0    IL_000c:  ldloc.1    IL_000d:  ldelem.i4    IL_000e:  call       void [mscorlib]System.Console::WriteLine(int32)    IL_0013:  ldloc.1    IL_0014:  ldc.i4.1    IL_0015:  add    IL_0016:  stloc.1    IL_0017:  ldloc.1    IL_0018:  ldloc.0    IL_0019:  ldlen    IL_001a:  conv.i4    IL_001b:  blt.s      IL_000b    IL_001d:  ret  } 
   | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
   | .method private hidebysig static void  ForTest2() cil managed {      .maxstack  2    .locals init ([0] int32[] a, // 变量声明 (局部变量表)            [1] int32 len,            [2] int32 i)   IL_0000:  ldc.i4.5    IL_0001:  newarr     [mscorlib]System.Int32    IL_0006:  stloc.0    IL_0007:  ldloc.0    IL_0008:  ldlen    IL_0009:  conv.i4    IL_000a:  stloc.1    IL_000b:  ldc.i4.0    IL_000c:  stloc.2    IL_000d:  br.s       IL_001b    IL_000f:  ldloc.0    IL_0010:  ldloc.2    IL_0011:  ldelem.i4    IL_0012:  call       void [mscorlib]System.Console::WriteLine(int32)    IL_0017:  ldloc.2    IL_0018:  ldc.i4.1    IL_0019:  add    IL_001a:  stloc.2    IL_001b:  ldloc.2    IL_001c:  ldloc.1    IL_001d:  blt.s      IL_000f    IL_001f:  ret  } 
   | 
 
对比上述的 IL code,确实临时存储数组长,能够少在 for 的比较进行中少进行一定的操作,无需将数组从局部变量表(Local Variable Table)入操作数栈 (Operand stack),并执行 ldlen 获取数组长。 但要注意, JIT 编译器知道 Length 是 Array 类的属性,生成的代码中只会调用该属性一次,结果会存储到临时变量中,此后的检查中调用的都是此临时变量。不需要自己用局部变量做缓存,这样既没有性能提升,还可能造成可读性下降。
参阅
CLR via C# (第四版) 16.7 数组的内部工作原理
注释
Ildasm.exe(IL 反汇编程序)
一般,该工具位于 NETFX 4.7.2 Tools 中
C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.7.2 Tools\x64\ildasm.exe