使用“ var”会影响性能吗?


230

早些时候我问了一个问题,为什么我看到如此多的示例使用该var关键字并得到答案,尽管仅对于匿名类型是必需的,但仍使用它来使代码写得“更快速” /更容易并且“正因为如此”。

通过该链接(“ C#3.0-Var Is't Objec”),我看到它已var被编译为IL中正确的类型(您将在文章中途看到它)。

我的问题是,使用var关键字take可以获取多少IL代码(如果有的话),并且如果在各处使用它,它的性能甚至还可以接近吗?


1
很久以前回答的问题,只是想针对var添加一件事-尽管在编译时已解决,但是如果要查找该类型的所有用法,Visual Studio的“查找所有引用”和Resharper的“查找用法”都无法正确发现该问题。 -它不会被修复,因为它太慢了。
2015年

@KolA声明的变量var绝对可以与Visual Studio 2019中的“查找所有引用”一起使用,因此,如果它已损坏,则已修复。但是我可以确认它可以追溯到Visual Studio 2012,所以我不确定为什么您声称它不起作用。
Herohtar

@Herohtar尝试使用以下代码“类X {} X GetX(){return new X();} void UseX(){var x = GetX();}”,并查找对X的所有引用,“ var x = GetX( )”部分未突出显示-到目前为止,在最新的VS2019中,这就是我的意思。如果您使用“ X x = GetX()”而不是var,则突出显示该颜色
KolA

1
@KolA啊,我明白您的意思- 当您在上使用“查找所有参考”时,var不会被视为参考。有趣的是,如果你使用“查找所有引用”上的这句话,它带你到引用(尽管它依然不会列出的语句)。此外,当光标位于上时,它将突出显示同一文档中的所有实例(反之亦然)。XXvarXvarvarX
Herohtar

Answers:


316

var关键字没有额外的IL代码:对于非匿名类型,生成的IL应该相同。如果编译器由于无法弄清您打算使用哪种类型而无法创建该IL,则会出现编译器错误。

唯一的技巧是,var如果要手动设置类型,则将推断出您可能已经选择接口或父类型的确切类型。


8年后更新

由于我的理解发生了变化,我需要对此进行更新。我现在认为,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变量。

从性能的角度来看,大多数情况下您不会注意到。但是,在某些情况下,第三行的性能可能不如第一行的性能快。当您继续使用bar3变量时,编译器可能无法以相同的方式分派方法调用。

请注意,抖动有可能(可能甚至)消除这种差异,但不能保证。通常,var就性能而言,您仍应考虑将其作为非因素。当然,它根本不像使用dynamic变量。但是,说它根本没有改变可能夸大了它。


23
不仅要IL是相同的-它相同的。var i = 42; 编译为与int i = 42完全相同的代码;
Brian Rasmussen

15
@BrianRasmussen:我知道您的帖子很旧,但是我认为var i = 42;(推断类型为int)与并不相同long i = 42;。因此,在某些情况下,您可能对类型推断做出错误的假设。如果该值不合适,则可能导致难以捉摸的/边缘情况下的运行时错误。因此,在值没有显式类型时将其显式仍然是一个好主意。因此,例如,var x = new List<List<Dictionary<int, string>()>()>()可以接受,但var x = 42有些含糊,应写成int x = 42。但是对于每个人……
纳尔逊·罗瑟梅尔

50
@NelsonRothermel:var x = 42; 并不模棱两可。整数文字是这种类型的int。如果想要字面量很长,请编写var x = 42L;
Brian Rasmussen'5

6
嗯,IL在C#中代表什么?我从未真正听说过它。
puretppc 2014年

15
在示例3行代码的行为不同的示例中,第一行不会编译。都可以编译的第二和第三行执行的操作完全相同。如果Foo返回a List而不是an IList,则所有三行都将编译,但第三行的行为类似于第一行,而不是第二行。
Servy

72

正如Joel所说,编译器在编译时就可以算出var应该是什么类型,实际上,这只是编译器执行的一种保存按键的技巧,例如

var s = "hi";

被替换

string s = "hi";

由编译器生成任何IL之前。生成的IL将与您键入字符串完全相同


26

正如没有人提到反射器...

如果编译以下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


14

因此,很明显,这是一种惰性编码样式。如果可以的话,我更喜欢本机类型。我将多一点“噪音”,以确保我正在编写和阅读代码/调试时我所认为的内容。*耸肩*


1
那只是您的主观观点,而不是有关性能问题的答案。正确的答案是它对性能没有影响。我投了赞成票
Anders

这根本没有回答是否var会影响性能的问题。您只是在说明人们是否应该使用它。
Herohtar

从值稍后推断类型,例如,从int 5切换到float 5.25绝对会导致性能问题。*耸耸肩*
ChrisH '19

不,那不会造成任何性能问题;您会在任何期望类型为变量的地方出现构建错误,int因为它无法自动转换float,但这与您显式使用int然后更改为完全相同float。无论如何,您的答案仍然无法回答“使用var会影响性能吗?”的问题。(尤其是在生成的IL方面)
Herohtar,

8

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

var i = 42;

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

int i = 42;

正如您链接到的帖子所说,它被编译为相同的类型。这不是运行时检查或其他需要额外代码的检查。编译器只是弄清楚类型必须是什么,并使用它。


是的,但是如果以后您i = i-someVar和someVar = 3.3怎么办。我现在是一个Int。最好明确一点,不仅要让编译器在发现缺陷方面先行一步,而且还应尽量减少运行时错误或减慢进程的类型转换。*耸耸肩*这也使代码更易于自我描述。我已经做了很长时间了。只要有选择,我每次都会使用带有显式类型的“嘈杂”代码。
克里斯·H

5

使用var没有运行时性能成本。虽然,我怀疑会因为编译器需要推断类型而导致编译性能损失,尽管这很可能可以忽略不计。


10
RHS必须无论如何都要计算其类型-编译器会捕获不匹配的类型并抛出错误,因此我认为这并不是真正的代价。
吉米

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,而是依靠intellisense更快地声明对象。

例:

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

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

例:

var coolObject = GetCoolObject(param1, param2);

如果您是为学生写作,那么就吃自己的狗粮,并始终以相同的“正确”方式书写。学生们通常会百分百地保持直言不讳的态度,并会逐渐采用沿途养成的任何草率习惯。$ .02
ChrisH '17

1

“无常”是人们喜欢或讨厌的事物之一(例如区域)。但是,与区域不同,创建匿名类时绝对需要使用var。

对我来说,当您直接更新对象时,var是有意义的,例如:

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

话虽如此,您可以轻松地做到:

Dictionary<string, string> dict = 新的和智能的将在这里为您填充其余内容。

如果您只想使用特定的接口,那么除非您要调用的方法直接返回该接口,否则不能使用var。

Resharper似乎全都使用“ var”,这可能会促使更多的人这样做。但是我有点同意,如果您正在调用方法,则很难阅读,而且名称返回的内容也不是很明显。

var本身并不会减慢任何速度,但是有一个警告是很多人都不会想到的。如果这样做,var result = SomeMethod();那么之后的代码将返回某种结果,您可以在此调用各种方法或属性或其他任何方法。如果SomeMethod()将其定义更改为其他类型,但仍符合其他代码期望的约定,则您刚刚创建了一个非常讨厌的错误(当然,如果没有单元/集成测试)。


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

问候

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.