簡體   English   中英

何時使用運行時類型信息?

[英]When to use run-time type information?

如果我有各種類的子類,以及對這些子類的實例進行操作的算法,並且如果算法的行為根據實例的特定子類略有不同,那么最常用的面向對象的方法是使用虛擬方法。

例如,如果子類是DOM節點,並且算法要插入子節點,則該算法根據父節點是DOM元素(可以有子節點)還是DOM文本(不能)來區別:所以insertChildren方法可以是DomNode基類中的虛擬(或抽象),並且在每個DomElementDomText子類中實現不同。

另一種可能性是為實例提供一個公共屬性,可以讀取其值:例如,算法可能會讀取DomNode基類的nodeType屬性; 或者另一個例子,您可能有不同類型(子類)的網絡數據包,它們共享一個公共數據包標頭,您可以讀取數據包標頭以查看它是什么類型的數據包。

我沒有太多使用運行時類型信息,包括:

  • C#中的isas關鍵字
  • 溯造型
  • 點網中的Object.GetType方法
  • C ++中的typeid運算符

當我添加一個取決於子類類型的新算法時,我傾向於在類層次結構中添加一個新的虛方法。

我的問題是,何時使用運行時類型信息而不是虛函數?

當沒有別的辦法時。 虛擬方法總是首選,但有時它們無法使用。 有幾個原因導致這種情況發生,但最常見的原因是您沒有要使用的類的源代碼,或者您無法更改它們。 當您使用舊系統或使用閉源商業庫時,通常會發生這種情況。

在.NET中,你可能還需要動態加載新的程序集,比如插件,你通常沒有基類,但必須使用像duck typing這樣的東西。

在C ++中,在其他一些不起眼的案例中(主要處理較差的設計選擇),RTTI是一種實現所謂多方法的方法

這種結構(“是”和“as”)對於Delphi開發人員來說非常熟悉,因為事件處理程序通常會將對象轉發給共同的祖先。 例如,事件OnClick傳遞唯一的argurment Sender:TObject,無論對象的類型是什么,無論是TButton,TListBox還是其他任何類型。 如果您想了解有關此對象的更多信息,則必須通過“as”訪問它,但為了避免異常,您可以在之前使用“is”進行檢查。 這種向下轉換允許通過嚴格的類類型檢查無法實現的對象和方法的設計類型綁定。 想象一下,如果用戶單擊Button或ListBox,您想要執行相同的操作,但如果它們為我們提供了不同的函數原型,則無法將它們綁定到相同的過程。

在更一般的情況下,對象可以調用通知對象例如已經改變的函數。 但事先它讓目的地“親自”(通過as和is)了解他,但不一定。 它通過將self作為所有對象的最常見祖先(Delphi案例中的TObject)來實現這一點

dynamic_cast <>,如果我沒記錯的話,取決於RTTI。 當一個對象被通過一個空指針傳遞(對於可能發生任何原因)有些晦澀外部接口也可能依賴RTTI。

話雖這么說,我在10年的專業C ++維護工作中還沒有看到typeof()。 (幸運)。

對於運行時類型檢查正常的情況,可以參考更有效的C#。

項3.使用運行時類型檢查專門化通用算法

只需指定新類型參數,即可輕松重用泛型。 具有新類型參數的新實例化意味着具有類似功能的新類型。

這一切都很棒,因為你寫的代碼較少。 然而,有時更通用意味着不利用更具體但明顯優越的算法。 C#語言規則考慮到了這一點。 只需要您認識到,當類型參數具有更強大的功能時,您的算法可以更高效,然后編寫該特定代碼。 此外,創建指定不同約束的第二個泛型類型並不總是有效。 通用實例化基於對象的編譯時類型,而不是運行時類型。 如果您沒有考慮到這一點,您可能會錯過可能的效率。

例如,假設您編寫了一個類,該類在通過IEnumerable <T>表示的項目序列上提供反向枚舉。 為了向后枚舉它,您可以迭代它並將項目復制到具有索引器訪問權限的中間集合(如List <T>),然后使用索引器訪問向后枚舉該集合。 但是如果你的原始IEnumerable是IList,為什么不利用它並提供更高效的方式(不復制到中間集合)來向后迭代項目。 所以基本上它是一個特殊的我們可以利用但仍然提供相同的行為(迭代序列向后)。

但一般來說,您應該仔細考慮運行時類型檢查,並確保它不違反Liskov Substituion Principle。

暫無
暫無

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

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