我的老板禁止我使用var
,因为它会导致拳击并减慢应用程序的速度。
这是真的吗?
答案 0 :(得分:52)
可能有效的方法是编写这两种方法:
public static void WithInt()
{
int x = 5;
Console.WriteLine(x);
}
public static void WithVar()
{
var x = 5;
Console.WriteLine(x);
}
编译,并使用ildasm
检查生成的CIL。告诉你的老板。
答案 1 :(得分:37)
根据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
答案 2 :(得分:32)
为什么有这么多人被愚蠢的老板诅咒?革命,兄弟们!
你的老板需要阅读文档。 var
使编译器通过查看初始化表达式的静态类型来确定变量类型。无论您是手动指定类型还是使用var
并让编译器为您解决它,它在运行时都没有丝毫差异。
更新在问题的评论中,Hans Passant问道
你能想到任何var初始化器吗? 这导致拳击而不使用 投?
强制进行此类转换的自包含表达式的示例是:
var boxedInt = new Func<int, object>(n => n)(5);
但这与:
完全相同object boxedInt = new Func<int, object>(n => n)(5);
换句话说,这与var
没有任何关系。我的初始化表达式的结果是object
,因此var
必须使用它作为变量的类型。它不可能是其他任何东西。
答案 3 :(得分:32)
这根本不是真的。
var
只是意味着“亲爱的编译器,我知道类型是什么,所以你也是,所以让我们继续吧。”
它使代码更短,有些人发现它更具可读性(其他人发现它更不易读),但是没有任何性能损失。
答案 4 :(得分:10)
也许你的老板是用于VARIANT
类型的旧Visual Basic(在&lt; = 6.0中)编程器。如果您没有在DIM
语句中明确指定变量的类型,那么如果我没记错的话,那就是VARIANT
union
。将这些变量传递给函数时,您可以将其视为一种“装箱”和“拆箱”。
有时人们会感到困惑。向你的老板询问他的Visual Basic战争故事。聆听,学习并同时获得一些同情!当您离开办公室时,您可以指出c#编译器在编译时将这些内容计算出来并且“装箱”不再是问题。
不要指望你的老板必须跟上语言/ API的最新变化。这不是愚蠢的。这是关于还有其他事情要做。比如他的工作。
修改:如下面的评论中所述,告诉您不要错误地使用var
可能不是他的工作......
答案 5 :(得分:3)
实际上,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循环和第一个using / while对不会导致垃圾(由于缺少装箱),但第二个使用/ while循环确实(因为它包装了返回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())
{
}
}
}
}
另请注意,将此项从“List”更改为“IList”将破坏此优化,因为IList只能推断出IEnumerator类型的接口正在返回。使用List变量,编译器可以更智能,并且可以看到唯一有效的返回值是[mscorlib] System.Collections.Generic.List`1 / Enumerator,因此可以优化调用来处理此问题。
虽然我知道这是一个非常有限的情况,但它可能是一个重要的情况,特别是在没有进行完全增量垃圾收集并暂停线程进行标记/扫描的设备上。