簡體   English   中英

如何擺脫虛擬表? 密封類

[英]How to get rid of virtual table? Sealed class

據我所知,制作類sealed擺脫了VTable中的查找或我錯了嗎? 如果我使類sealed ,這是否意味着類層次結構中的所有虛方法也標記為sealed

例如:

public class A {
    protected virtual void M() { ........ }
    protected virtual void O() { ........ }
}

public sealed class B : A {
    // I guess I can make this private for sealed class
    private override void M() { ........ }
    // Is this method automatically sealed? In the meaning that it doesn't have to look in VTable and can be called directly?

    // Also what about O() can it be called directly too, without VTable?
}

“我想我可以將它作為密封課程的私人”

您無法更改繼承層次結構中的訪問修飾符。 這意味着如果方法在基類中是public ,則不能將其設置為privateinternal或在派生類中protected 只有在將方法聲明為new方法時,才能更改修改器:

private new void M() { ........ }

據我所知,制作類密封擺脫了VTable中的查找或我錯了嗎?

密封類是層次結構中的最后一個,因為您無法繼承它。 虛擬表可以與sealed類一起使用,以防sealed類覆蓋基類中的某些方法。

如果我使類密封,這是否意味着類層次結構中的所有虛方法也標記為密封?

你可以看到IL代碼:

.method family hidebysig virtual            // method is not marked as sealed
    instance void M () cil managed 
{        
    .maxstack 8

    IL_0000: nop 
    IL_0001: ret value
}  

方法未標記為已sealed 即使您明確將此方法標記為已sealed您也將獲得相同的IL代碼。

此外,沒有任何理由因為標記方法sealedsealed類。 如果類是sealed你不能繼承它,所以,你不能繼承它的方法。

關於虛擬表 - 如果方法覆蓋並且您從虛擬表中刪除它,則永遠不能在繼承層次結構中使用它,因此,沒有理由覆蓋方法並且永遠不會在繼承層次結構中使用它。

首先要注意的是,C#通常會對引用類型上的任何實例方法進行虛擬調用,即使該方法不是虛擬的。 這是因為有一個C#規則,在不是.NET規則的空引用上調用方法是非法的(如果在空引用上調用方法並且該方法本身不訪問字段,則在原始CIL中或虛擬方法,它工作正常)和使用callvirt是一種廉價的方式來執行該規則。

在某些情況下,C#將為非虛擬實例調用生成call而不是callvirt ,在這種情況下,引用顯然不為null。 特別是與obj?.SomeMethod()以來的?. 表示已經發生了空檢查,如果SomeMethod()不是虛擬的,則會編譯為call 這只發生在?. 既然編譯的代碼?. 可以做那個檢查,而if (obj != null){obj.SomeMethod();}不會發生,因為代碼編譯. 不知道它是否在進行空檢查。 涉及的邏輯是非常本地化的

在CIL級別可以跳過虛擬表查找虛擬方法。 這是base調用的工作方式; 編譯到一個call在該基地的實施方法,而不是一個的callvirt 通過擴展構造obj?.SomeMethod() ,其中SomeMethod是虛擬的並且是密封的(無論是單獨的還是因為obj的類型是密封的),理論上可以將其編譯為對最派生類型的實現的call 雖然聲明類型和密封類型之間的層次結構中的類添加或刪除覆蓋,但仍需要進行一些額外的檢查以確保它仍能正常工作。 它需要一些全局的層次結構知識(並保證知識不會改變,這意味着當前正在編譯的程序集中的所有類型)才能使優化變得安全。 收益很小。 並且在大多數情況下仍然不可用,因為大多數時候即使在非虛擬呼叫上使用callvirt也是如此。

我認為sealed不會影響編譯器生成調用的方式,而且大多數情況下肯定不會影響它。

抖動可以自由地應用更多的知識,但是如果有的話,差異將會非常小。 我當然建議將你知道不會被覆蓋的類標記為sealed ,如果抖動使用了那么那就太好了,但我推薦它的主要原因不是性能而是正確性。 如果你某個地方試圖覆蓋你標記為sealed的類,那么要么A.你剛剛改變了設計並且知道你必須刪除sealed (.5秒工作以刪除它)或B.你已經在一個地方做了一件事,你確定你不會在另一個地方。 暫時重新考慮是件好事。

如果我使類密封,這是否意味着類層次結構中的所有虛方法也標記為密封?

它們被認為是密封的,就像明確標記為密封一樣。

密封意味着您無法繼承或覆蓋它。 如果你不能從B類繼承,你就無法覆蓋方法M.至於查找我不認為你保存它。 在你的示例中,調用方法O必須從類A中查找方法並調用它。 那是通過vtable進行的。

密封類或方法將無效,對BM()任何調用都將是虛擬的。

如果我不得不打賭為什么會這樣,我會說它因為M()A聲明,覆蓋它不會使該方法“屬於” B

如果檢查生成的代碼的IL ,您將看到相關指令是callvirt instance string namespace.A::M() ,因此,即使B被密封,調用也必須是虛擬的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM