На самом деле, 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
Обратите внимание, что 2-й (назначение var)знает, что это возвращаемое значение является типом оценки (структурой) из List и может использовать его более эффективно - даже если контракт из List.GetEnumerator возвращает IEnumerator.Это приведет к удалению операции бокса с этой структурой и приведет к более эффективному коду.
Вот почему, например, в следующем коде цикл foreach и первое использование / while пара не вызывает мусора (из-заиз-за отсутствия бокса), но 2-й цикл с использованием / 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, и поэтому он может оптимизировать вызов для обработки этого.
Хотя я понимаю, что это очень ограниченный случай, он может быть важным, особенно на устройствах, которые не выполняют полную инкрементную сборку мусора и приостанавливают ваши потоки для выполнения метки / очистки.