2010-08-24 2 views
37

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

Это правда?

+2

'var x = 0' vs' int x = 0' - это один из случаев, когда вывод типа не стоит того. Но «* sign * f ** k эти бессознательные персидские наркоманы» в любом случае. – delnan

+18

Не связанное с программированием. Обсудите спорт в другом месте! –

+12

wow. это такие люди, как вы, у которых есть веская причина, чтобы мочиться в вашем боссом кофе :) –

ответ

50

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

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

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

Compile, и использовать ildasm для изучения полученного КСС. Покажите своего босса.

редактировать@ck has done all but the last step для вас :)

+10

+1 для IL-доказательств – annakata

+5

+1 за то, что он смог показать своему боссу, что он не прав – Spooks

+9

Там идет ваш рейз .... :( – ChaosPandion

31

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

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

Update В комментарии по этому вопросу, Ханс Passant спрашивает

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

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

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

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

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

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

+1

Ну, тип inferer, теоретически, может помешать коробочному типу для литерала. Но я думаю, что нет. ++ для тиража. – delnan

+0

Даже если бы босс был виноват в микро-оптимизации, и даже если это было неправдой, он был виноват в том, что он предпочитает работу над обслуживанием, что почти повсеместно неправильно. Почему всегда отвечают люди, которые не знают, о чем они говорят? – annakata

+0

@ delnan: Какая причина для этого? –

31

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

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

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

+8

«дорогой компилятор ...» приятно :) Но по порядку ведения я думаю, что это означает «Дорогой компилятор, мне все равно, что это такое, вы решаете» – annakata

+5

@annakata: «вы решили» тоже хорошо, пока "Удиви меня!" не может быть и речи :) –

+0

Если помешанный тип (при условии, что это правильно, конечно) вас удивляет, вам следует по-разному взглянуть - по крайней мере, в Haskell (только язык, использующий язык, который я когда-либо использовал) это обычно означает, что вы что-то испортило. – delnan

35

Исходя из ответа 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  
+9

Подождите ... какая разница, я вижу? О, неважно, это было просто пятно на моем мониторе :) –

+0

Хехе .. приятно :-) – cjk

10

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

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

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

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

+4

+1 для объяснения поведения боссов. Это звучит вполне вероятно. Но если босс считает, что его знание остается актуальным без его поддержки, то он, по крайней мере, немного тупой. И если он проводит время, рассказывая программистам, какие ключевые слова использовать (хотя они знают лучше, чем он есть), то он тоже не выполняет свою работу. – Niki

+0

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

+0

@Brian - Точно. Если вы собираетесь диктовать ключевые слова, вам лучше знать, о чем вы говорите. Если вы не хотите идти в ногу с последними техническими разработками, все в порядке, но не микроменеджмент, чтобы диктовать, какие ключевые слова приемлемы. –

3

На самом деле, переменная может также избежать бокс в некоторых очень специфических случаях ,

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 

Обратите внимание, что вторая одна (уступка вар) знает, что это возвращаемое значение является ValueType (структура) внутри списка и может более эффективно использовать - хотя контракт из List.GetEnumerator возвращает IEnumerator. Это приведет к удалению операции бокса в этой структуре и приведет к более эффективному коду.

Вот почему, например, в следующем коде цикл foreach и первая с использованием пары/while не вызывают мусор (из-за отсутствия бокса), но второй цикл использования/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()) 
      { 
      } 
     } 
    } 
} 

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

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

Смежные вопросы