在VB.NET,C#等中,单点规则是否会自动优化到代码中?

Pan*_*sis 1 .net c# vb.net compiler-optimization

在VB.NET,C#等中,这样的东西不会得到优化,不是吗?

'Bad
a.B.C.DoSomething1()
a.B.C.DoSomething2()
a.B.C.X = 5
DoSomething3(a.B.C.D)

'Good
Dim cachedReference As ClassOfC = a.B.C
cachedReference.DoSomething1()
cachedReference.DoSomething2()
cachedReference.X = 5
DoSomething3(cachedReference.D)
Run Code Online (Sandbox Code Playgroud)

至少在ECMA类型的语言中,这是一个很好的习惯,可以最小化它的次数a,然后转到它的B字段/属性,然后最终转到它的C字段/属性.我认为这通常是任何典型的面向对象或过程语言的经验法则,除非至少有一个非常可靠的期望,它将被编译器/ jit /等进行优化.无论如何.在典型的.NET中如何处理,特别是对于VB.NET和C#?

例如,这似乎没有提到一点规则,至少与这两种语言有关:https://msdn.microsoft.com/en-us/library/ms973839.aspx.另一方面,如果它对于属性一般这样做我会感到非常惊讶,因为那些是有效的方法.如果它做到这一点似乎更有意义,只要它们是字段甚至可能是完全无关紧要的属性.

Jon*_*ase 5

好的,我继续为了好奇而尝试了它.

鉴于以下课程:

public class Foo
{
    public Bar Bar { get; set;}
}

public class Bar
{
    public Baz Baz { get; set;}
}

public class Baz
{
    public string One { get; set; } = string.Empty;
    public string Two { get; set; } = string.Empty;
    public bool BothPopulated() => !(string.IsNullOrWhiteSpace(One) || string.IsNullOrWhiteSpace(Two));
}

public class FooFactory
{
    public static Foo Create() => new Foo { Bar = new Bar { Baz = new Baz { One = "Hello", Two = "World" } } };
}
Run Code Online (Sandbox Code Playgroud)

以下方法:

void Main()
{
    var foo = FooFactory.Create();
    var cached = foo.Bar.Baz;
    Console.WriteLine(cached.One);
    Console.WriteLine(cached.Two);
    Console.WriteLine(cached.BothPopulated());

    var fooTwo = FooFactory.Create();
    Console.WriteLine(fooTwo.Bar.Baz.One);
    Console.WriteLine(fooTwo.Bar.Baz.Two);
    Console.WriteLine(fooTwo.Bar.Baz.BothPopulated());
}
Run Code Online (Sandbox Code Playgroud)

LinqPad报告在发布模式下主要发送的IL为

IL_0000:  call        UserQuery+FooFactory.Create
IL_0005:  callvirt    UserQuery+Foo.get_Bar
IL_000A:  callvirt    UserQuery+Bar.get_Baz
IL_000F:  dup         
IL_0010:  callvirt    UserQuery+Baz.get_One
IL_0015:  call        System.Console.WriteLine
IL_001A:  dup         
IL_001B:  callvirt    UserQuery+Baz.get_Two
IL_0020:  call        System.Console.WriteLine
IL_0025:  callvirt    UserQuery+Baz.BothPopulated
IL_002A:  call        System.Console.WriteLine // <- End of cached portion
IL_002F:  call        UserQuery+FooFactory.Create
IL_0034:  dup         
IL_0035:  callvirt    UserQuery+Foo.get_Bar
IL_003A:  callvirt    UserQuery+Bar.get_Baz
IL_003F:  callvirt    UserQuery+Baz.get_One
IL_0044:  call        System.Console.WriteLine 
IL_0049:  dup         
IL_004A:  callvirt    UserQuery+Foo.get_Bar
IL_004F:  callvirt    UserQuery+Bar.get_Baz
IL_0054:  callvirt    UserQuery+Baz.get_Two
IL_0059:  call        System.Console.WriteLine
IL_005E:  callvirt    UserQuery+Foo.get_Bar
IL_0063:  callvirt    UserQuery+Bar.get_Baz
IL_0068:  callvirt    UserQuery+Baz.BothPopulated
IL_006D:  call        System.Console.WriteLine
IL_0072:  ret 
Run Code Online (Sandbox Code Playgroud)

因此,看起来你每次都不钻掘属性来保存一些callvirts.这是否会在运行时对JIT产生任何可衡量的影响,这不是我能回答的问题,但它确实留下了较小的IL足迹.

我怀疑它对运行时性能基本上没有影响.