Will using 'var' affect performance?
早些时候,我问了一个问题,为什么我看到这么多的例子使用
在这个链接之后("c 3.0-var不是objec"),我看到
我的问题是,如果有的话,使用
对于
唯一的诀窍是
随着我的理解的改变,我需要更新这个。我现在认为,在方法返回接口的情况下,
1 2 3 4 | IList<int> Foo() { return Enumerable.Range(0,10).ToList(); } |
考虑这三行代码来调用方法:
1 2 3 | List<int> bar1 = Foo(); IList<int> bar = Foo(); var bar3 = Foo(); |
三者都按预期编译和执行。但是,前两行不完全相同,第三行将与第二行匹配,而不是第一行。因为
从性能的角度来看,大多数情况下你不会注意到。但是,在某些情况下,第三行的性能可能不如第一行的性能快。当您继续使用
请注意,抖动可能(甚至可能)会消除这一差异,但这并不能保证。一般来说,在性能方面,您仍然应该将
正如Joel所说,编译器在编译时计算出var应该是什么类型,实际上它只是编译器保存击键的一个技巧,例如
1 | var s ="hi"; |
被替换为
1 | string s ="hi"; |
在生成任何IL之前由编译器执行。生成的IL将与键入的字符串完全相同。
因为还没有人提到反射镜…
如果您编译以下C代码:
1 2 3 4 5 6 7 | static void Main(string[] args) { var x ="hello"; string y ="hello again!"; Console.WriteLine(x); Console.WriteLine(y); } |
然后在上面使用反射镜,你会得到:
1 2 3 4 5 6 7 8 | // Methods private static void Main(string[] args) { string x ="hello"; string y ="hello again!"; Console.WriteLine(x); Console.WriteLine(y); } |
因此,答案显然是没有运行时性能受到影响!
对于以下方法:
1 2 3 4 5 6 |
IL输出如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | { .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编译器在编译时推断
我觉得你读的东西不太明白。如果它被编译成正确的类型,那么就没有区别。当我这样做的时候:
1 | var i = 42; |
编译器知道它是一个int,并生成代码,就好像我已经写了
1 | int i = 42; |
正如你链接到的帖子所说,它被编译成相同的类型。它不是运行时检查或其他需要额外代码的操作。编译器只是计算出类型必须是什么,并使用它。
使用var没有运行时性能开销。不过,我怀疑编译性能开销,因为编译器需要推断类型,尽管这很可能是可忽略的。
我总是在网络文章或指南文章中使用var这个词。
在线文章的文本编辑器的宽度很小。
如果我写这个:
1 | SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName coolClass = new SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName(); |
您将看到上面呈现的代码前文本太长,并且从框中流出,它会被隐藏。读者需要向右滚动才能看到完整的语法。
这就是为什么我总是在Web文章写作中使用关键字var的原因。
1 |
整个呈现的预代码只适合屏幕。
实际上,对于对象的声明,我很少使用var,我依靠intellisense更快地声明对象。
例子:
1 |
但是,为了从方法返回对象,我使用var更快地编写代码。
例子:
1 | var coolObject = GetCoolObject(param1, param2); |
如果编译器可以自动进行类型推断,那么性能就不会有任何问题。这两个都将生成相同的代码
但是,如果您正在动态构造类型(linq…),那么
"var"是人们爱或恨的事物之一(比如地区)。但是,与区域不同,在创建匿名类时,var是绝对必要的。
对我来说,当您直接更新一个对象时,var是有意义的,比如:
1 |
也就是说,你可以很容易地做到:
新的智能感知系统将在这里为您填补剩下的部分。
如果您只想使用特定的接口,那么除非您调用的方法直接返回接口,否则不能使用var。
Resharper似乎站在了使用"var"的一边,这可能会促使更多人这样做。但我也同意,如果您正在调用一个方法,并且名称返回的内容并不明显,那么就很难阅读。
VaR本身并没有减缓任何事情的发展,但有一点需要注意,这是许多人所没有想到的。如果您执行
这取决于情况,如果您尝试使用,下面将显示此代码。
表达式被转换为"object"并大大降低了性能,但这是一个孤立的问题。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | 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。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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(); } |
如果您希望执行此代码,请使用下面的代码,并获得时间差。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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(); } |
当做