早些时候我问了一个关于 why I see so many examples use the var
keyword 的问题,得到的答案是,虽然它只对匿名类型是必需的,但它仍然用于使编写代码“更快”/更容易和“仅仅因为”。
在 this link ("C# 3.0 - Var Isn't Objec") 之后,我看到 var
在 IL 中被编译为正确的类型(您将在文章的中途看到它)。
我的问题是,如果有的话,使用 var
关键字的 IL 代码还需要多少,如果在任何地方都使用它,它是否接近于代码性能的可测量水平?
var
声明的 @KolA 变量绝对可以与 Visual Studio 2019 中的“查找所有引用”一起使用,因此如果它被破坏,它已经被修复。但我可以确认它可以追溯到 Visual Studio 2012,所以我不确定你为什么声称它不起作用。
X
上使用“查找所有引用”时,var
不会被视为对 X
的引用。有趣的是,如果您在该语句中的 var
上使用“查找所有引用”,它将显示对 X
的引用(尽管它仍然不会列出 var
语句)。此外,当光标位于 var
上时,它将突出显示同一文档中 X
的所有实例(反之亦然)。
var
关键字没有额外的中间语言 (IL) 代码:对于非匿名类型,生成的 IL 应该是相同的。如果编译器因为无法确定您打算使用的类型而无法创建该 IL,则会出现编译器错误。
唯一的技巧是,如果您要手动设置类型,var
将推断出您可能选择了接口或父类型的确切类型。
正如 Joel 所说,编译器在编译时计算出 var 应该是什么类型,实际上这只是编译器为保存击键而执行的一个技巧,例如
var s = "hi";
被取代
string s = "hi";
在生成任何 IL 之前由编译器执行。生成的 IL 将与您键入字符串完全相同。
由于还没有人提到反射器...
如果编译以下 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
C# 编译器在编译时推断 var
变量的真实类型。生成的 IL 没有区别。
所以,要清楚,这是一种懒惰的编码风格。如果可以选择,我更喜欢原生类型;我将采取额外的“噪音”来确保我正在编写和阅读我认为我在代码/调试时的内容。 *耸耸肩*
var
是否会影响性能的问题。你只是在表达你对人们是否应该使用它的看法。
int
类型的变量的地方遇到构建错误,因为它无法自动转换 float
,但如果您明确使用 int
然后进行更改,就会发生同样的事情到 float
。无论如何,您的回答仍然没有回答“使用 var
会影响性能吗?”的问题。 (特别是在生成的 IL 方面)
我认为你没有正确理解你所读的内容。如果它被编译为正确的类型,那么就没有区别。当我这样做时:
var i = 42;
编译器知道它是一个 int,并像我写的那样生成代码
int i = 42;
正如您链接到的帖子所说,它被编译为相同的类型。这不是运行时检查或其他任何需要额外代码的东西。编译器只是弄清楚类型必须是什么,并使用它。
使用 var 没有运行时性能成本。不过,我怀疑编译器需要推断类型时会产生编译性能成本,尽管这很可能可以忽略不计。
如果编译器可以进行自动类型推断,那么性能不会有任何问题。这两个都将生成相同的代码
var x = new ClassA();
ClassA x = new ClassA();
但是,如果您正在动态构造类型(LINQ ...),那么 var
是您唯一的问题,并且还有其他机制可以比较以说明惩罚是什么。
我总是在网络文章或指南文章中使用 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);
“var”是人们喜欢或讨厌的事物之一(例如区域)。但是,与区域不同,在创建匿名类时 var 是绝对必要的。
对我来说,当你直接新建一个对象时, var 是有意义的,比如:
var dict = new Dictionary<string, string>();
话虽如此,您可以轻松地做到:
Dictionary<string, string> dict =
new 和 intellisense 将在此处为您填写其余部分。
如果您只想使用特定接口,则不能使用 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();
}
问候
var i = 42;
(推断类型为 int)与long i = 42;
不同。因此,在某些情况下,您可能会对类型推断做出错误的假设。如果值不合适,这可能会导致难以捉摸/边缘情况运行时错误。出于这个原因,当值没有显式类型时,显式可能仍然是一个好主意。例如,var x = new List<List<Dictionary<int, string>()>()>()
是可以接受的,但var x = 42
有点模棱两可,应该写成int x = 42
。但是对于每个人来说...var x = 42;
不是模棱两可的。整型文字属于int
类型。如果你想要一个字面量 long,你可以写var x = 42L;
。Foo
返回List
而不是IList
,则所有三行都将编译,但第三行 的行为类似于第一行,而不是第二行。