将使用'var'影响性能?

前面我问了一个关于为什么我看到这么多的例子使用__abc0关键字的问题,得到的答案是,虽然它只对匿名类型是必要的,但它仍然被用于使代码编写“更快”/更容易,并且“只是因为”。

这个链接(“c# 3.0 - Var不是对象”)之后,我看到var在IL中被编译为正确的类型(你将在本文的中途看到它)。

我的问题是,如果有的话,使用var关键字的IL代码会占用多少,如果它在任何地方都被使用,它是否会接近代码性能的可测量水平?

120930 次浏览

var关键字没有额外的中间语言(IL)代码:生成的IL对于非匿名类型应该是相同的。如果编译器不能创建那个IL,因为它不能找出你想要使用的类型,你就会得到一个编译器错误。

唯一的技巧是var将推断出一个确切的类型,如果你手动设置类型,你可能已经选择了一个Interface或父类型。

c#编译器在编译时推断var变量的真实类型。产生的IL没有差异。

正如Joel所说,编译器在编译时处计算出var应该是什么类型,实际上这只是编译器执行的一个技巧,以节省击键,所以举例来说

var s = "hi";

被替换为

string s = "hi";

通过编译器在任何IL生成之前。生成的IL将是完全,就像你输入string一样。

如果编译器可以自动进行类型推断,那么就不会有任何性能问题。这两种方法都会生成相同的代码

var    x = new ClassA();
ClassA x = new ClassA();

然而,如果你是动态构造类型(LINQ…),那么var是你唯一的问题,还有其他机制可以比较,以说明惩罚是什么。

我认为你没有正确理解你所读的内容。如果它被编译为正确的类型,那么没有区别。当我这样做的时候:

var i = 42;

编译器知道是一个int型,生成的代码就像我写的一样

int i = 42;

正如你链接到的帖子所说,它将编译转换为相同的类型。它不是运行时检查或任何需要额外代码的东西。编译器只是找出类型必须是什么,然后使用它。

使用var没有运行时性能成本。尽管如此,我怀疑编译器需要推断类型会有编译性能成本,尽管这很可能可以忽略不计。

因为还没有人提到反射器…

如果你编译以下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);
}

所以答案显然是没有运行时性能的影响!

以下方法:

   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

我总是在网络文章或指南写作中使用var这个词。

网络文章的文本编辑器宽度较小。

如果我这样写:

SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName coolClass = new SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName();

你会看到上面渲染的预代码文本太长,流出框,它被隐藏。读者需要向右滚动才能看到完整的语法。

这就是为什么我总是在网络文章写作中使用关键字var。

var coolClass = new SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName();

整个渲染的预代码正好适合屏幕。

在实践中,对于声明对象,我很少使用var,我依靠智能感知来更快地声明对象。

例子:

SomeCoolNamespace.SomeCoolObject coolObject = new SomeCoolNamespace.SomeCoolObject();

但是,对于从方法返回对象,我使用var来更快地编写代码。

例子:

var coolObject = GetCoolObject(param1, param2);

因此,明确地说,这是一种懒惰的编码风格。如果可以选择,我更喜欢原生类型;我将利用这一点额外的“噪音”来确保我在代码/调试时所编写和阅读的内容完全符合我的想法。耸肩*

“var”是人们要么喜欢要么讨厌的东西之一(就像区域一样)。不过,与区域不同,var在创建匿名类时是绝对必要的。

对我来说,当你直接像这样更新一个对象时,var是有意义的:

var dict = new Dictionary<string, string>();

话虽如此,你可以很容易地做到:

Dictionary<string, string> dict =新的和智能感知将填补你在这里休息。

如果你只想使用一个特定的接口,那么你不能使用var,除非你调用的方法直接返回该接口。

Resharper似乎是在使用“var”的一边,这可能会促使更多人这样做。但我同意,如果你在调用一个方法,并且它的名称返回的是什么并不明显,那就更难读了。

Var本身并没有减慢速度,但有一个警告,很多人都没有想到。如果你执行var result = SomeMethod();,那么在那之后的代码将期望返回某种类型的结果,你将调用各种方法或属性或其他东西。如果SomeMethod()将其定义更改为其他类型,但它仍然满足其他代码所期望的契约,那么您就创建了一个非常严重的错误(当然,如果没有单元/集成测试)。

这取决于情况,如果您尝试使用,此代码如下。

表达式被转换为“OBJECT”,性能大大降低,但这是一个孤立的问题。

代码:

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

问候