2016-09-19 16 views
1

在VB.NET,C#等,這樣的事情不會得到優化,會嗎?在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) 

在ECMA-型語言,這至少是一個良好的習慣是在儘量減少它多少次去a,然後去其B字段/屬性,最後去其C場/屬性。我認爲這通常是任何典型的面向對象或過程語言的經驗法則,除非至少有一個非常穩固的期望,即它只是像編譯器/ jit/etc那樣進行優化。無論如何。典型的.NET是如何處理的,尤其是VB.NET和C#?

例如,這似乎沒有提到單點規則,至少與這兩種語言中的任何一種有關:https://msdn.microsoft.com/en-us/library/ms973839.aspx。另一方面,如果一般情況下這樣做屬性,我會非常驚訝,因爲這些都是有效的方法。這似乎是更有意義的,如果它是這樣做的,當且僅當它們是字段可能甚至是完全無關緊要的屬性。

+4

在.net中沒有像這樣的規則。一般來說,假設/指導原則是屬性重量輕,如果有的話,應該很難做出返回結果的工作。因此,通過財產鏈或屬性鏈上的方法調用財產**應該**在績效上幾乎沒有影響。如果屬性結果不像'DateTime.Now'那樣是冪等的,那麼情況可能並非總是如此,在這種情況下,您應該首先獲取實例,然後重用它。方法(方法鏈接)也是如此,因爲方法的創建可能很昂貴。簡短的回答=沒有規則;它取決於 – Igor

+2

我對單點規則不是特別熟悉,但它似乎是一種幫助優化縮小JS文件的啓發式方法,這是一種語法大小的優化。 .NET代碼被編譯爲一箇中間語言,稍後由JITer編譯。如果上述IL看起來特別不同或導致任何實際的運行時問題,我會感到驚訝。 –

+1

至於編譯器優化,如果您編譯發佈模式(啓用優化代碼),一些調用是內聯的,但它取決於編譯器的壓縮。再次,可能(*取決於代碼*)不是性能上的增益(如果有的話)。 – Igor

回答

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" } } }; 
} 

,按照下述方式:

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

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 

所以它看起來像你通過每次不穿透屬性來節省一些callvirts。在運行時對於JIT是否有任何可測量的影響是我不能回答的,但它似乎留下了更小的IL足跡。

我懷疑它對運行時性能基本上沒有影響。