关于c#:使用’var’会影响性能吗?

Will using 'var' affect performance?

早些时候,我问了一个问题,为什么我看到这么多的例子使用var关键字,并得到了这样的答案:虽然它只是匿名类型所必需的,但它仍然被用来使代码的编写"更快"、"更容易"和"仅仅因为"。

在这个链接之后("c 3.0-var不是objec"),我看到var被编译成IL中的正确类型(您将在中途的文章中看到)。

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


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

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

8年后更新

随着我的理解的改变,我需要更新这个。我现在认为,在方法返回接口的情况下,var可能会影响性能,但您可能会使用精确的类型。例如,如果您有此方法:

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

三者都按预期编译和执行。但是,前两行不完全相同,第三行将与第二行匹配,而不是第一行。因为Foo()的签名是返回IList的,所以编译器将以这种方式构建bar3变量。

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

请注意,抖动可能(甚至可能)会消除这一差异,但这并不能保证。一般来说,在性能方面,您仍然应该将var视为一个非因素。当然,这根本不像使用dynamic变量。但如果说这一点都不起作用,那可能是夸大了这一点。


正如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
   private static void StringVsVarILOutput()
    {
        var string1 = new String(new char[9]);

        string string2 = new String(new char[9]);
    }

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编译器在编译时推断var变量的真实类型。生成的IL没有区别。


我觉得你读的东西不太明白。如果它被编译成正确的类型,那么就没有区别。当我这样做的时候:

1
var i = 42;

编译器知道它是一个int,并生成代码,就好像我已经写了

1
int i = 42;

正如你链接到的帖子所说,它被编译成相同的类型。它不是运行时检查或其他需要额外代码的操作。编译器只是计算出类型必须是什么,并使用它。


使用var没有运行时性能开销。不过,我怀疑编译性能开销,因为编译器需要推断类型,尽管这很可能是可忽略的。


我总是在网络文章或指南文章中使用var这个词。

在线文章的文本编辑器的宽度很小。

如果我写这个:

1
SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName coolClass = new SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName();

您将看到上面呈现的代码前文本太长,并且从框中流出,它会被隐藏。读者需要向右滚动才能看到完整的语法。

这就是为什么我总是在Web文章写作中使用关键字var的原因。

1
var coolClass = new SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName();

整个呈现的预代码只适合屏幕。

实际上,对于对象的声明,我很少使用var,我依靠intellisense更快地声明对象。

例子:

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

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

例子:

1
var coolObject = GetCoolObject(param1, param2);


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

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

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


"var"是人们爱或恨的事物之一(比如地区)。但是,与区域不同,在创建匿名类时,var是绝对必要的。

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

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

也就是说,你可以很容易地做到:

新的智能感知系统将在这里为您填补剩下的部分。

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

Resharper似乎站在了使用"var"的一边,这可能会促使更多人这样做。但我也同意,如果您正在调用一个方法,并且名称返回的内容并不明显,那么就很难阅读。

VaR本身并没有减缓任何事情的发展,但有一点需要注意,这是许多人所没有想到的。如果您执行var result = SomeMethod();,那么后面的代码将期望返回某种结果,您可以调用各种方法或属性或其他东西。如果SomeMethod()将其定义更改为其他类型,但它仍然满足其他代码所期望的契约,那么您就创建了一个非常讨厌的bug(当然,如果没有单元/集成测试)。


这取决于情况,如果您尝试使用,下面将显示此代码。

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

当做