[英]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
,則不能將其設置為private
或internal
或在派生類中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代碼。
此外,沒有任何理由因為標記方法sealed
在sealed
類。 如果類是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.