2010-01-07 31 views
30

我正在寫一個C#網絡庫(主要是作爲一個學習鍛鍊,如果有人實際上最終使用它,因爲我」這不是對我來說太重要當然,解決方案已經在那裏了)。設計諮詢 - 當使用「虛擬」和「密封」有效

我對自己的結構非常愉快,到目前爲止,我還提供客戶機/服務器,可以在原始字節通過套接字,或者稍微複雜一點,通過序列化消息對象溝通的幾層。

我遇到的問題是什麼時候我應該聲明方法,屬性或事件sealed,virtual或沒有限定符。

我知道所有的這些事情 - sealed防止一類的繼承,或方法的進一步覆蓋。 virtual將允許通過方法重寫的多態行爲。

由於我在設計一個類庫,但是,我不確定使用這些。這是一個可擴展性的問題,我認爲......我提供了一些接口,一個或兩個抽象類,以及我的庫的消費者使用或擴展的一些具體實現,但是我很難決定何時它是一個「好主意」明確禁止派生類或允許覆蓋功能。

在設計我的課程供其他人使用時,請牢記一切指示或建議嗎?

This questionthis one有點幫助,因爲是this one,但由於我正在編寫一個可分發的庫,我試圖涵蓋所有的基礎。

+1

(回覆評論說,刪除了!) @(節錄) - 不正確的;您可以將'override'標記爲'sealed'以防止進一步覆蓋。 – 2010-01-07 20:28:43

回答

24

Microsoft Design Guidelines for Developing Class Libraries開始,尤其是Extensibility部分,您將在此找到有關virtual memberssealing的文章。

引用,在這裏:

  • 不要讓虛擬的成員,除非你有一個很好的理由這樣做,你知道所有有關設計,測試的成本,並保持虛擬成員。
  • 爲虛擬會員更好地選擇受保護的可訪問性而不是公共可訪問性。公共成員應通過調用受保護的虛擬成員來提供可擴展性(如果需要)。

  • 如果沒有充分的理由,請勿封印課程。

  • 請勿在密封類型上聲明受保護或虛擬成員。
  • 考慮您覆蓋的密封件。

閱讀完整的文章,雖然。

+0

將其留給MSDN。這些文章是非常有用的,我會一定讓他們收藏。謝謝! :) – Sapph 2010-01-07 21:04:18

+2

該MSDN文章說,不要在性能密集型領域使用代表和事件,但仍有多準確(考慮文章是從2005年開始的)?從我公認的有限的測試中,調用委託與調用接口方法一樣快,這比靜態方法慢兩倍。 – JulianR 2010-01-07 22:10:16

+0

朱利安,這改變了.NET 2.0:http://www.sturmnet.org/blog/2005/09/01/所以你是對的;那已經過時了。 – 2010-01-07 22:17:22

2

通過繼承來設計可擴展性的類通常並不容易。當你說出某些東西時,你說這個類真的不適合實現繼承(也許你正在做一堆低級別的東西,例如不安全的代碼)。

爲了獲得最大的composibility(在數學意義上),我建議密封一切,這不是一個抽象類。實際上,這是在語言中實現代數數據類型(在這種情況下爲總和類型)的方式(請查閱http://blog.lab49.com/archives/3011以獲得令人興奮的文章)。

+1

非常有趣的文章! – Sapph 2010-01-07 21:04:56

6

在我看來,你不能真正預見你的用戶最終會使用它。因此,除非有一些內部行爲你不希望用戶瞎搞(即他們需要知道在你這樣做之前必須先設置它,等等),否則通常不是一個好主意。

至於虛擬,你會傾向於做什麼虛擬最終用戶可能要重寫。事件減少了對虛擬功能的需求,但仍然有時候你想讓它們變成虛擬的。一般來說,您需要考慮給定成員函數如何可能需要由最終用戶定製。

3

聲明一個方法虛擬意味着某種契約:你的課程承認它可以被覆蓋並預期。

通常有一個明確的理由來虛擬一些東西。有疑問時:不要。

密封恰恰相反,你可以說你不希望它被覆蓋了。第二個原因可能是性能,但我不會太快。

2

有爭議的觀點:C# classes should be sealed by default

如果這個類不是被設計爲繼承的,而且你沒有想過通過潛在的陷阱或記錄如何正確繼承,那麼如果人們重寫方法,你的類很可能會以奇怪的方式失敗。如果你不封閉班級,你告訴班級的客戶,你可以從班級中派生出來,你將來必須支持班級的這個「界面」,以避免打破選擇從你的班級繼承的客戶。這會限制你將來如何修改你的班級。

因此,默認情況下封印類,除非您明確地要允許它。如果您確實想要允許它,請確保您記錄哪些方法應通過虛擬方法被重寫,以及重寫的方法必須執行哪些操作以確保類繼續工作(如調用基本方法)。

+1

我相信對於密封類也有很小的性能優勢,因爲運行時確實知道它不需要查找覆蓋等,並且可以相應地進行優化。 – 2010-01-07 22:52:26

19

首先,我同意其他答案,建議積極密封任何並非專門設計用於擴展的東西。我不同意你需要一個理由來封印一些東西;相反,你需要一個理由讓東西不被打開。

你的問題是非常籠統的,所以這裏有一個普遍的答案:你的圖書館大概是特徵的集合,它可以被用作解決用戶問題的工具。你圖書館的每個功能都可能存在,因爲你做了一些研究,發現有一個需要解決的用戶問題,併爲他們解決了這個問題。

這些功能中的每一個都有一定的相關成本。其中一些成本是過去的 - 您花在設計,實施,測試,調試和運輸代碼上的時間。其中一些成本尚未到來:維護,讀取錯誤報告等等。還有更多微妙的成本,比如保持與現有功能之一的向後兼容性,將會增加明天實施新功能的成本。

可擴展性也是一個功能。這是一個功能,如果你弄錯了,成本幾乎完全在未來。像處理任何其他功能一樣對待它:弄清楚它是否是用戶真正需要的功能,以及它的好處是否會支付其成本。如果你不能清楚地評估可擴展性的好處或成本,那麼它可能會不小心執行它。

+0

有用的方法來看看它,謝謝! – Sapph 2010-01-08 01:00:30

+0

+1「你需要一個理由離開東西」 – Doval 2014-01-28 02:05:44

5

我曾經說過很多次「該死的爲什麼這門課被封了!」,但我從來沒有說過「天哪 - 我希望他們封了那堂課!」

很有可能有一天比你更好的程序員會想要擴展你的課程,並且知道他們在做什麼。我認爲有很好的理由來封印是很少見的。

1

您有興趣衝突這裏。

  • 如果你想使它容易對您的呼叫,以測試他們的代碼(例如做測試驅動開發),您的用戶需要能夠模擬類。
    • 所以所有的方法都必須是虛擬的,或者你必須有你實現的包含所有方法的接口。
  • 但是,如果您希望清楚說明您在用戶可能創建的任何子類上提供的合同,那麼您應該遵守Microsoft準則。
    • 不要讓會員虛擬除非你有一個很好的理由這樣做,你知道所有有關設計,測試和維護虛擬成員 的成本實在是可惜沒有標記一個辦法虛擬的方法來說,除了嘲笑之外,你不希望任何客戶端代碼實現它。