早些时候我问了一个关于why I see so many examples use the var
keyword的问题并得到了答案,虽然它只是匿名类型所必需的,但它仍然用于使代码“更快”/更容易和“只是因为”。
关注this link ("C# 3.0 - Var Isn't Objec")我看到var
被编译为IL中的正确类型(您将在文章中间看到它)。
我的问题是IL代码在使用var
关键字时有多少(如果有的话),如果在任何地方使用它,它是否会接近于代码性能的可衡量水平?< / p>
答案 0 :(得分:298)
var
关键字没有额外的IL代码:对于非匿名类型,生成的IL应该是相同的。如果编译器无法创建该IL,因为它无法确定您打算使用的类型,则会出现编译器错误。
唯一的技巧是var
将推断一个确切的类型,如果您要手动设置类型,可以选择接口或父类型。
我需要更新这个,因为我的理解已经改变了。我现在相信var
可能会在方法返回接口的情况下影响性能,但您会使用精确类型。例如,如果您有这种方法:
IList<int> Foo()
{
return Enumerable.Range(0,10).ToList();
}
考虑这三行代码来调用该方法:
List<int> bar1 = Foo();
IList<int> bar = Foo();
var bar3 = Foo();
所有三个按预期编译和执行。但是,前两行不完全相同,第三行与第二行匹配,而不是第一行。因为Foo()
的签名是返回IList<int>
,所以编译器将构建bar3
变量。
从表现的角度来看,大多数情况下你都不会注意到。但是,有些情况the performance of the third line may not be quite as fast as the performance of the first。当您继续使用bar3
变量时,编译器可能无法以相同的方式调度方法调用。
请注意,可能(甚至可能)抖动可以消除这种差异,但不能保证。 一般来说,您仍然应该将var
视为性能方面的非因素。当然,完全不像使用dynamic
变量。但是说它根本没有任何区别可能会夸大它。
答案 1 :(得分:70)
正如Joel所说,编译器在编译时处理var应该是什么类型,实际上它只是编译器为保存击键而执行的一个技巧,例如
var s = "hi";
取代
string s = "hi";
在生成任何IL之前,编译器 。生成的IL 与完全相同,就像输入字符串一样。
答案 2 :(得分:25)
由于没人提到反射器......
如果您编译以下C#代码:
static void Main(string[] args)
{
var x = "hello";
string y = "hello again!";
Console.WriteLine(x);
Console.WriteLine(y);
}
然后在上面使用反射器,你得到:
// Methods
private static void Main(string[] args)
{
string x = "hello";
string y = "hello again!";
Console.WriteLine(x);
Console.WriteLine(y);
}
所以答案显然没有达到运行时性能!
答案 3 :(得分:17)
对于以下方法:
private static void StringVsVarILOutput()
{
var string1 = new String(new char[9]);
string string2 = new String(new char[9]);
}
IL输出是这样的:
{
.method private hidebysig static void StringVsVarILOutput() cil managed
// Code size 28 (0x1c)
.maxstack 2
.locals init ([0] string string1,
[1] string string2)
IL_0000: nop
IL_0001: ldc.i4.s 9
IL_0003: newarr [mscorlib]System.Char
IL_0008: newobj instance void [mscorlib]System.String::.ctor(char[])
IL_000d: stloc.0
IL_000e: ldc.i4.s 9
IL_0010: newarr [mscorlib]System.Char
IL_0015: newobj instance void [mscorlib]System.String::.ctor(char[])
IL_001a: stloc.1
IL_001b: ret
} // end of method Program::StringVsVarILOutput
答案 4 :(得分:15)
所以,要清楚,这是一种懒惰的编码风格。考虑到选择,我更喜欢原生类型;我将采取额外的“噪音”,以确保我正在编写和阅读我认为我在代码/调试时的确切内容。 *耸肩*
答案 5 :(得分:13)
C#编译器在编译时推断出var
变量的真实类型。生成的IL没有区别。
答案 6 :(得分:8)
我认为你没有正确理解你所读到的内容。如果它被编译为正确的类型,那么 没有区别。当我这样做时:
var i = 42;
编译器知道它是一个int,并生成代码就像我写的一样
int i = 42;
正如您链接的帖子所说,它会编译到同一类型。它不是运行时检查或其他需要额外代码的东西。编译器只是弄清楚类型必须是什么,并使用它。
答案 7 :(得分:5)
使用var没有运行时性能成本。虽然,我怀疑是否需要编译性能成本,因为编译器需要推断出类型,尽管这很可能是可以忽略不计的。
答案 8 :(得分:3)
如果编译器可以进行自动类型推理,那么性能就不会有任何问题。这两个都会生成相同的代码
var x = new ClassA();
ClassA x = new ClassA();
但是,如果您正在动态构建类型(LINQ ...),那么var
是您唯一的问题,还有其他机制可供比较,以便说出什么是惩罚。
答案 9 :(得分:3)
我总是在网络文章或指南中使用var这个词。
在线文章的文本编辑器的宽度很小。
如果我写这个:
SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName coolClass = new SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName();
您将看到上面呈现的前代码文本太长并且开箱即用,它会被隐藏。读者需要向右滚动才能看到完整的语法。
这就是为什么我总是在网络文章着作中使用关键字var。
var coolClass = new SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName();
整个渲染的预编码恰好适合屏幕。
在实践中,为了声明对象,我很少使用var,我依靠intellisense来更快地声明对象。
示例:
SomeCoolNamespace.SomeCoolObject coolObject = new SomeCoolNamespace.SomeCoolObject();
但是,对于从方法返回对象,我使用var来更快地编写代码。
示例:
var coolObject = GetCoolObject(param1, param2);
答案 10 :(得分:2)
对我来说,当你直接创建一个对象时,var是有意义的:
var dict = new Dictionary<string, string>();
话虽如此,你可以很容易地做到:
Dictionary<string, string> dict =
new和intellisense将在这里填写其余的内容。
如果您只想使用特定的接口,那么除非您正在调用的方法直接返回接口,否则不能使用var。
Resharper似乎全都在使用“var”,这可能会推动更多人这样做。但我有点同意,如果你正在调用一个方法,那么它就更难以阅读,并且名称返回的内容并不明显。
var本身不会减慢任何事情,但有一点需要注意,这并不是很多人想到的。如果您执行var result = SomeMethod();
,那么之后的代码会在您调用各种方法或属性或其他任何内容时返回某种结果。如果SomeMethod()
将其定义更改为其他类型,但它仍然符合其他代码所期望的合同,那么您刚刚创建了一个非常讨厌的错误(当然,如果没有单元/集成测试)。
答案 11 :(得分:0)
这取决于情况,如果您尝试使用此代码,则如下所示。
表达式被转换为“对象”并降低了性能,但这是一个孤立的问题。
代码:
public class Fruta
{
dynamic _instance;
public Fruta(dynamic obj)
{
_instance = obj;
}
public dynamic GetInstance()
{
return _instance;
}
}
public class Manga
{
public int MyProperty { get; set; }
public int MyProperty1 { get; set; }
public int MyProperty2 { get; set; }
public int MyProperty3 { get; set; }
}
public class Pera
{
public int MyProperty { get; set; }
public int MyProperty1 { get; set; }
public int MyProperty2 { get; set; }
}
public class Executa
{
public string Exec(int count, int value)
{
int x = 0;
Random random = new Random();
Stopwatch time = new Stopwatch();
time.Start();
while (x < count)
{
if (value == 0)
{
var obj = new Pera();
}
else if (value == 1)
{
Pera obj = new Pera();
}
else if (value == 2)
{
var obj = new Banana();
}
else if (value == 3)
{
var obj = (0 == random.Next(0, 1) ? new Fruta(new Manga()).GetInstance() : new Fruta(new Pera()).GetInstance());
}
else
{
Banana obj = new Banana();
}
x++;
}
time.Stop();
return time.Elapsed.ToString();
}
public void ExecManga()
{
var obj = new Fruta(new Manga()).GetInstance();
Manga obj2 = obj;
}
public void ExecPera()
{
var obj = new Fruta(new Pera()).GetInstance();
Pera obj2 = obj;
}
}
高于ILSPY的结果。
public string Exec(int count, int value)
{
int x = 0;
Random random = new Random();
Stopwatch time = new Stopwatch();
time.Start();
for (; x < count; x++)
{
switch (value)
{
case 0:
{
Pera obj5 = new Pera();
break;
}
case 1:
{
Pera obj4 = new Pera();
break;
}
case 2:
{
Banana obj3 = default(Banana);
break;
}
case 3:
{
object obj2 = (random.Next(0, 1) == 0) ? new Fruta(new Manga()).GetInstance() : new Fruta(new Pera()).GetInstance();
break;
}
default:
{
Banana obj = default(Banana);
break;
}
}
}
time.Stop();
return time.Elapsed.ToString();
}
如果您希望执行此代码,请使用下面的代码,并获取时间差。
static void Main(string[] args)
{
Executa exec = new Executa();
int x = 0;
int times = 4;
int count = 100000000;
int[] intanceType = new int[4] { 0, 1, 2, 3 };
while(x < times)
{
Parallel.For(0, intanceType.Length, (i) => {
Console.WriteLine($"Tentativa:{x} Tipo de Instancia: {intanceType[i]} Tempo Execução: {exec.Exec(count, intanceType[i])}");
});
x++;
}
Console.ReadLine();
}
致谢