C#中的var关键字是否会导致装箱?

我的老板禁止我使用var因为它会导致拳击和放缓的应用程序。

真的吗?

可能有效的方法是编写这两种方法:

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

编译并使用ildasm来检查生成的CIL。 展示你的老板。

编辑 @ck已经做了所有,但你最后一步 🙂

接下来从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 

为什么有这么多人骂老板笨蛋? 革命,兄弟!

你的老板需要阅读文件。 var使编译器通过查看初始化expression式的静态types来找出variablestypes。 不pipe你是手动指定types,还是使用var并让编译器为你弄明白,它在运行时并没有丝毫的区别。

更新在这个问题下的评论,汉斯帕斯坦问

你能想到的任何变种初始值没有使用强制转换导致拳击?

强制进行这种转换的自包含expression式的示例是:

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

但是这与以下内容完全相同:

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

换句话说,这与var没有任何关系。 我的初始化expression式的结果是object ,因此var必须使用它作为variables的types。 这不可能是别的。

这根本不是真的。

var只是意思是“亲爱的编译器,我知道types是什么,你也是,所以让我们继续前进吧。”

它使得代码更短,有些人觉得这更可读(其他人觉得它不太可读),但是没有任何性能损失。

也许你的老板是一个旧的Visual Basic(如<= 6.0)程序员用于VARIANTtypes。 如果你没有在你的DIM语句中明确指定你的variables的types,那么这是一个VARIANT ,如果我记得正确的话,这是一种union 。 将这些variables传递给函数时,您可以将其视为“装箱”和“拆箱”。

有时候人们会感到困惑。 向你的老板询问他的Visual Basic战争故事。 聆听,学习,同时获得一些同情! 当你离开办公室的时候,你可以指出c#编译器在编译的时候会把这些东西看出来,而“拳击”已经不是什么问题了。

不要指望你的老板必须跟上语言/ API的最新变化。 这不是愚蠢的。 这是关于有其他的事情要做。 他的工作,例如。

编辑:正如下面的评论中指出的,但是,告诉你不要使用var的错误原因可能不是他的工作…

其实,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内部的一个valuetype(struct),并且可以更有效地使用它 – 即使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只能推断IEnumeratortypes的接口回来。 使用Listvariables,编译器可以变得更聪明,并且可以看到唯一有效的返回值是[mscorlib] System.Collections.Generic.List`1 / Enumerator,因此可以优化调用来处理这个问题。

虽然我知道这是一个非常有限的情况,但它可能是一个重要的问题,尤其是在不进行完整增量垃圾收集并暂停线程执行标记/扫描的设备上。