Подтвердить что ты не робот

Является ли ключевое слово var в С# причиной бокса?

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

Это правда?

4b9b3361

Ответ 1

Подход, который может работать, заключается в том, чтобы написать эти два метода:

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

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

Скомпилируйте и используйте ildasm, чтобы изучить полученный CIL. Покажите своего босса.

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

Ответ 2

Следуя ответу от Aakash, вот 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      

Ответ 3

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

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

Обновление. В комментарии по этому вопросу Ханс Пассант спрашивает

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

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

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

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

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

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

Ответ 6

Собственно, 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) знает, что это возвращаемое значение является valueetype (struct) изнутри List и может более эффективно использовать его, даже если контракт из List.GetEnumerator возвращает IEnumerator. Это приведет к удалению операции бокса в этой структуре и приведет к более эффективному коду.

Вот почему, например, в следующем коде цикл foreach и первый, используя пару /while, не вызывают мусор (из-за отсутствия бокса), но второй цикл использования /while (так как он блокирует return struct):

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())
            {
            }
        }
    }
}

Обратите также внимание, что изменение этого параметра из "списка" на "IList" нарушит эту оптимизацию, поскольку IList может только вывести, что возвращается интерфейс типа IEnumerator. С переменной List компилятор может быть более умным и может видеть, что единственным допустимым возвращаемым значением является [mscorlib] System.Collections.Generic.List`1/Enumerator и поэтому может оптимизировать вызов для обработки этого.

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