使用 'var' 会影响性能吗?

245
之前我提出了一个问题,关于为什么有那么多使用 var 关键字的示例,得到的答案是尽管只有匿名类型需要,但它仍然被用来使编写代码更加“快速” / 简单,并且“只是因为这样做。” 在阅读此链接(“C#3.0-Var不是Objec”)后,我发现 var 在 IL 中被编译成正确的类型(您将在文章中部看到它)。
我的问题是,如果到处都使用 var 关键字,会增加多少 IL 代码量,如果使用会对代码的性能产生可衡量的影响?

1
问题很久以前就已经回答了,只是想再补充一点关于 var 的事情 - 尽管在编译时被解析,但如果您想查找类型的所有用法,则 Visual Studio 的“查找所有引用”和 Resharper 的“查找用法”无法正确识别它,并且这个问题不会得到修复,因为修复它会太慢。 - KolA
@KolA 使用 var 声明的变量在 Visual Studio 2019 中绝对可以使用“查找所有引用”功能,因此如果曾经出现过问题,那么现在已经被修复了。但是我可以确认它可以在 Visual Studio 2012 上使用,所以我不确定为什么你声称它不能工作。 - Herohtar
@Herohtar,请尝试以下代码“class X { } X GetX() { return new X(); } void UseX() { var x = GetX(); }”,并查找所有对X的引用,最新的VS2019目前没有突出显示“var x = GetX()”部分。如果使用“X x = GetX()”而不是var,则会突出显示。 - KolA
1
@KolA 哦,我明白你的意思了——当你在 X 上使用“查找所有引用”时,var 不会被视为对 X 的引用。有趣的是,如果你在该语句中使用“查找所有引用”来查找 var,它 显示对 X 的引用(尽管它仍然不会列出 var 语句)。此外,当光标位于 var 上时,它将突出显示同一文档中所有 X 的实例(反之亦然)。 - Herohtar
12个回答

331

对于 var 关键字,没有额外的中间语言(IL)代码:对于非匿名类型,生成的 IL 应该是相同的。如果编译器无法创建该 IL,因为它无法确定您打算使用的类型,您将会得到一个编译器错误。

唯一的诀窍是 var 将推断出一个精确的类型,而您可能会手动设置一个接口或父类型。


26
IL 不仅应该是相同的 - 它就是相同的。变量 i = 42; 编译出来的代码与 int i = 42; 完全相同。 - Brian Rasmussen
18
@BrianRasmussen: 我知道你的帖子已经有些年头了,但我认为var i = 42;(推断类型为int)并不等同于long i = 42;。因此,在某些情况下,您可能会对类型推断做出错误的假设。如果值不合适,这可能导致难以捉摸的/边缘情况的运行时错误。因此,当值没有明确的类型时,清晰表达类型仍然是一个好主意。例如,var x = new List<List<Dictionary<int, string>()>()>是可以接受的,但var x = 42有点含糊不清,应该写成int x = 42。但每个人都有自己的想法... - Nelson Rothermel
60
@NelsonRothermel说:var x = 42;并不含糊。整型字面量属于类型int。如果你想要一个长整型字面量,你应该写成var x = 42L; - Brian Rasmussen
8
在C#中,IL代表什么?我之前从未听说过它。 - puretppc
17
在你提到的三行代码示例中,第一行无法编译。第二和第三行都可以编译,它们做的事情完全相同。如果Foo返回一个List而不是IList,那么这三行代码都可以编译,但第三行会像第一行一样而不是像第二行。 - Servy
显示剩余10条评论

79

正如Joel所说,编译器在编译时确定了变量应该是哪种类型,实际上这只是编译器为了节省击键而执行的技巧,所以例如:

var s = "hi";

被替换成

string s = "hi";

在产生任何 IL 之前编译器会对其进行处理。生成的 IL 与您键入 string 时完全相同。


32

既然还没有人提到反射器(reflector)...

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

因此,答案显然是没有运行时性能损失!


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

15

因此,明确地说,这是一种懒惰的编码风格。如果有选择的话,我更喜欢使用本地类型;我愿意多写一点"噪音"以确保在编写和调试代码时我能够准确地阅读和理解它。*耸肩*


3
这只是你主观的看法,不是关于性能问题的答案。正确的答案是它对性能没有影响。我投票关闭。 - Anders
这并没有回答var是否会对性能产生影响的问题;你只是陈述了你对人们是否应该使用它的观点。 - Herohtar
从值中推断类型,例如从int 5切换到float 5.25,可能会导致性能问题。 耸肩 - ChrisH
不会造成任何性能问题;在任何期望类型为“int”的变量的地方,您将获得构建错误,因为它无法自动转换“float”,但如果您明确使用“int”然后更改为“float”,那么情况完全相同。无论如何,您的答案仍然没有回答“使用var是否影响性能?”这个问题(特别是在生成的IL方面)。 - Herohtar

15

C#编译器在编译时推断var变量的真实类型。生成的IL代码没有区别。


8

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

var i = 42;

编译器“知道”它是一个整数,并生成代码,就好像我已经写了这样的代码。
int i = 42;

正如你所链接的帖子所说,它被编译成相同的类型。这不是运行时检查或需要额外代码的其他内容。编译器只需确定类型必须是什么,并使用该类型。

1
对啊,但如果后来你 i = i - someVar,而 someVar = 3.3。现在 i 是 Int。最好是明确指定类型,不仅可以让编译器更早地发现缺陷,还可以最小化运行时错误或处理缓慢的类型转换。耸肩这也使代码更易于自我描述。我已经做了很长时间了。如果有选择,我会选择带有明确类型的“嘈杂”代码。 - ChrisH

5

使用var不会增加运行时性能成本。然而,我认为编译器需要推断类型,因此可能会有编译性能成本,但这很可能是可以忽略不计的。


11
RHS必须计算其类型-编译器会捕捉不匹配的类型并抛出错误,因此在这方面没有实际成本,我想。 - Jimmy

3
如果编译器可以进行自动类型推断,则性能不会有任何问题。这两者将生成相同的代码。
var    x = new ClassA();
ClassA x = new ClassA();

然而,如果你是动态构建类型(如LINQ ...),那么var就是你唯一的选择,没有其他机制可以用来比较以确定惩罚。


3

我常在网站文章或指南中使用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);

如果你是为学生写作,那么请自己先试用一下,并且始终以相同的“正确”方式进行编写。学生通常会完全照字面理解并深信不疑,他们会开始使用沿途学到的任何不规范的习惯。这是我的建议。 - ChrisH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接