Ключевое слово var в C # вызывает бокс? - PullRequest
38 голосов
/ 24 августа 2010

Мой босс запрещает мне использовать var, так как это вызовет бокс и замедлит работу приложения.

Это правда?

Ответы [ 6 ]

52 голосов
/ 24 августа 2010

Подход, который может сработать, - написать эти два метода:

public static void WithInt()
{
    int x = 5;
    Console.WriteLine(x);
}

public static void WithVar()
{
    var x = 5;
    Console.WriteLine(x);
}

Compile и использовать ildasm для проверки созданного CIL.Покажите своему боссу.

edit @ ck выполнил все, кроме последнего шага для вас:)

37 голосов
/ 24 августа 2010

Исходя из ответа Аакаша, вот IL: (спасибо LINQPad )

WithInt:
IL_0000:  ldc.i4.5    
IL_0001:  stloc.0     
IL_0002:  ldloc.0     
IL_0003:  call        System.Console.WriteLine
IL_0008:  ret         

WithVar:
IL_0000:  ldc.i4.5    
IL_0001:  stloc.0     
IL_0002:  ldloc.0     
IL_0003:  call        System.Console.WriteLine
IL_0008:  ret      
32 голосов
/ 24 августа 2010

Это совсем не так.

var просто означает «дорогой компилятор, я знаю, что это за тип, и вы тоже, так что давайте просто продолжим.»

Это делаеткод короче, и некоторые находят его более читабельным (другие считают его менее читаемым), но при этом никакого снижения производительности не происходит.

32 голосов
/ 24 августа 2010

Почему так много людей прокляты глупыми боссами?Революция, братья!

Ваш начальник должен прочитать документацию.var заставляет компилятор выяснить тип переменной, посмотрев на статический тип выражения инициализации.Это не имеет никакого значения во время выполнения, указываете ли вы тип вручную или используете var и позволяете компилятору выяснить это для вас.

Обновление В комментарии подХанс Пассант спрашивает:

Можете ли вы вспомнить любой инициализатор var, который вызывает бокс без использования приведения?

Пример автономного выражения, которое вызывает такоепреобразование:

var boxedInt = new Func<int, object>(n => n)(5);

Но это просто идентично:

object boxedInt = new Func<int, object>(n => n)(5);

Другими словами, это не имеет никакого отношения к var.Результат моего выражения инициализатора object, следовательно, var должен использовать это как тип переменной.Это не могло быть чем-то еще.

10 голосов
/ 24 августа 2010

Возможно, ваш начальник - старый программист Visual Basic (как в <= 6.0), используемый для типа <code>VARIANT.Если вы не указали тип своей переменной явно в своем выражении DIM, это был VARIANT, что является своего рода union, если я правильно помню.Вы можете рассматривать это как своего рода «упаковку» и «распаковку» при передаче таких переменных в функции.

Иногда люди путаются.Спросите своего босса о его военных рассказах Visual Basic.Слушай, учись и получай сочувствие одновременно!Когда вы выходите из офиса, вы можете заметить, что компилятор c # выясняет это во время компиляции и что «бокс» больше не является проблемой.

Не ожидайте, что вашему боссу придется не отставатьновейшие изменения в языках / API.Дело не в том, чтобы быть глупым.Речь идет о других делах.Его работа, например.

Редактировать: Однако, как отмечается в комментариях ниже, указывать, что вы не должны использовать var по неправильным причинам, вероятно, не его работа ...

3 голосов
/ 13 августа 2013

На самом деле, 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, и поэтому он может оптимизировать вызов для обработки этого.

Хотя я понимаю, что это очень ограниченный случай, он может быть важным, особенно на устройствах, которые не выполняют полную инкрементную сборку мусора и приостанавливают ваши потоки для выполнения метки / очистки.

...