【问题标题】:Will using 'var' affect performance?使用'var'会影响性能吗?
【发布时间】:2010-09-26 06:58:42
【问题描述】:

之前我问了一个关于 why I see so many examples use the varkeyword 的问题,得到的答案是,虽然它只对匿名类型是必需的,但它仍然用于使编写代码“更快”/更容易和“仅仅因为”。

this link ("C# 3.0 - Var Isn't Objec") 之后,我看到var 在 IL 中被编译为正确的类型(您将在文章的中途看到它)。

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

【问题讨论】:

  • 问题在很久以前就已经回答了,只是想针对 var 添加一件事 - 尽管在编译时已解决,但 Visual Studio 的“查找所有引用”和 Resharper 的“查找用法”没有正确发现它,如果你想要找到该类型的所有用法 - 它不会被修复,因为它太慢了。
  • @KolA 使用 var 声明的变量绝对可以与 Visual Studio 2019 中的“查找所有引用”一起使用,因此如果它被破坏,它已经被修复。但我可以确认它早在 Visual Studio 2012 就可以工作,所以我不确定你为什么声称它不起作用。
  • @Herohtar 尝试以下代码 "class X { } X GetX() { return new X(); } void UseX() { var x = GetX(); }" 并查找对 X 的所有引用,“var x = GetX()”位未突出显示-截至目前,在最新的 VS2019 中,这就是我的意思。如果您使用“X x = GetX()”而不是 var,它会突出显示
  • @KolA 啊,我明白你的意思了——当你在X 上使用“查找所有引用”时,var 不会被视为对X 的引用。有趣的是,如果您在该语句中对var 使用“查找所有引用”,它显示对X 的引用(尽管它仍然不会列出var 语句)。此外,当光标在var 上时,它将突出显示同一文档中X 的所有实例(反之亦然)。

标签: c# performance variables var


【解决方案1】:

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

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

【讨论】:

  • 不仅IL应该是相同的——它相同的。变量 i = 42;编译成与 int i = 42; 完全相同的代码
  • @BrianRasmussen:我知道您的帖子已经过时了,但我认为 var i = 42;(推断类型为 int)与 long i = 42; 不同。因此,在某些情况下,您可能会对类型推断做出错误的假设。如果值不合适,这可能会导致难以捉摸/边缘情况运行时错误。出于这个原因,当值没有显式类型时,显式可能仍然是一个好主意。例如,var x = new List<List<Dictionary<int, string>()>()>() 是可以接受的,但var x = 42 有点模棱两可,应该写成int x = 42。但对每个人来说...
  • @NelsonRothermel: var x = 42; 不是模棱两可。整型文字属于int 类型。如果你想要一个文字长,你写var x = 42L;
  • 嗯,IL 在 C# 中代表什么?我从来没有真正听说过。
  • 在您的 3 行代码行为不同的示例中,第一行 无法编译do 编译的第二行和第三行完全相同。如果Foo 返回List,而不是IList,则所有三行都将编译,但第三行表现得像第一行,而不是第二行。
【解决方案2】:

正如 Joel 所说,编译器在编译时计算出 var 应该是什么类型,实际上这只是编译器为保存击键而执行的一个技巧,例如

var s = "hi";

被替换为

string s = "hi";

编译器在生成任何 IL 之前。生成的 IL 将完全与您键入字符串一样。

【讨论】:

    【解决方案3】:

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

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

    所以答案显然不会影响运行时性能!

    【讨论】:

      【解决方案4】:

      对于以下方法:

         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
      

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

          所以,明确地说,这是一种懒惰的编码风格。如果可以选择,我更喜欢原生类型;我将采取额外的“噪音”来确保我正在编写和阅读我认为我在代码/调试时的内容。 *耸耸肩*

          【讨论】:

          • 这只是您的主观看法,而不是对性能问题的回答。正确的答案是它对性能没有影响。我投票赞成关闭
          • 这并不能回答var 是否会影响性能的问题;你只是在表达你对人们是否应该使用它的看法。
          • 稍后从值推断类型,例如,从 int 5 切换到 float 5.25,绝对会导致性能问题。 *耸耸肩*
          • 不,这不会导致任何性能问题;你会在任何需要 int 类型的变量的地方遇到构建错误,因为它不能自动转换 float,但如果你明确使用 int 然后更改,就会发生同样的事情到float。无论如何,您的回答仍然没有回答“使用var 会影响性能吗?”的问题。 (特别是在生成的 IL 方面)
          【解决方案7】:

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

          var i = 42;
          

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

          int i = 42;
          

          正如您链接到的帖子所说,它被 编译 为相同的类型。这不是运行时检查或其他任何需要额外代码的东西。编译器只是确定类型必须是什么,然后使用它。

          【讨论】:

          • 是的,但是如果稍后你 i = i - someVar 和 someVar = 3.3。我现在是一个 Int。最好是明确的,不仅可以让编译器在发现缺陷方面领先一步,而且可以最大限度地减少运行时错误或减慢进程的类型转换。 * 耸耸肩 * 它还使代码更适合自我描述。我已经这样做了很长时间。如果可以选择,我每次都会采用具有显式类型的“嘈杂”代码。
          【解决方案8】:

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

          【讨论】:

          • RHS 无论如何都必须计算其类型——编译器会捕获不匹配的类型并抛出错误,所以我认为这并不是真正的成本。
          【解决方案9】:

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

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

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

          【讨论】:

            【解决方案10】:

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

            【讨论】:

            • 如果你是为学生写作,那就吃你自己的狗粮,并始终以同样“正确”的方式写下去。学生们经常把事情 100% 逐字逐句记在心里,并且会开始使用他们在此过程中养成的任何草率习惯。 $.02
            【解决方案11】:

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

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

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

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

            Dictionary&lt;string, string&gt; dict =new 和 intellisense 将在此处为您填写其余部分。

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

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

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

            【讨论】:

              【解决方案12】:

              视情况而定,如果你尝试使用,下面的代码。

              表达式被转换为“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();
                  }
              

              问候

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2019-09-30
                • 1970-01-01
                • 2021-04-29
                • 2012-02-04
                • 2011-01-28
                • 1970-01-01
                • 1970-01-01
                • 2010-09-09
                相关资源
                最近更新 更多