其实 var 在一些非常特殊的情况下也可以避免装箱。
static void Main(string[] args)
{
List<Int32> testList = new List<Int32>();
IEnumerator<Int32> enumAsInterface = testList.GetEnumerator();
var enumAsStruct = testList.GetEnumerator();
}
导致以下 IL:
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Code size 27 (0x1b)
.maxstack 1
.entrypoint
.locals init (
[0] class [mscorlib]System.Collections.Generic.List`1<int32> testList,
[1] class [mscorlib]System.Collections.Generic.IEnumerator`1<int32> enumAsInterface,
[2] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32> enumAsStruct
)
IL_0000: nop
IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<int32>::GetEnumerator()
IL_000d: box valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>
IL_0012: stloc.1
IL_0013: ldloc.0
IL_0014: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<int32>::GetEnumerator()
IL_0019: stloc.2
IL_001a: ret
} // end of method Program::Main
请注意,第二个(var 赋值)知道此返回值是 List 内部的值类型(结构),并且可以更有效地使用它 - 即使 List.GetEnumerator 中的合同返回一个 IEnumerator。这将删除该结构上的装箱操作并产生更高效的代码。
这就是为什么,例如,在下面的代码中,foreach 循环和第一个 using/while 对不会导致垃圾(由于缺少装箱)但第二个 using/while 循环会(因为它将返回结构):
class Program
{
static void Main(string[] args)
{
List<Int32> testList = new List<Int32>();
foreach (Int32 i in testList)
{
}
using (var enumerator = testList.GetEnumerator())
{
while (enumerator.MoveNext())
{
}
}
using (IEnumerator<Int32> enumerator = testList.GetEnumerator())
{
while (enumerator.MoveNext())
{
}
}
}
}
还请注意,将其从“List”更改为“IList”将破坏此优化,因为 IList 只能推断 IEnumerator 类型的接口正在返回。使用 List 变量,编译器可以更智能,并且可以看到唯一有效的返回值是 [mscorlib]System.Collections.Generic.List`1/Enumerator,因此可以优化调用来处理这个问题。
虽然我知道这是一个非常有限的情况,但它可能很重要,尤其是在不进行完全增量垃圾收集并暂停线程以进行标记/清除的设备上。