C#中為什么不能繼承List?
問題:
每當構思我的程序時,我經常會有以下一連串的思考:
一個足球隊是一群足球運動員的集合,因此,我可以用下面的代碼來聲明:
var football_team = new List<FootballPlayer>();
這個list的排序順序就是這些運動員在花名冊里的順序。但是之后我又意識到除了運動員外,球隊還有別的屬性也需要記錄。比如說這個賽季的總得分,當前的預算,球服的顏色,用string類型表示的球隊名字,等等。
所以我認為:
既然一個球隊就是一群運動員的集合,但是額外的,它有一個名字(string類型)和一個當前總得分(int類型)。 .NET沒有提供一個用來存儲球隊的類,所以我得自己定義一個類。和現有結構最接近的是List<FootballPlayer>,所以我將繼承它:
class FootballTeam : List<FootballPlayer> { public string TeamName; public int RunningTotal }
但是結果是,我被提示不能繼承List<T>。 關于這個提示,我有兩點疑惑:
為什么不能?
顯然List某種程度是對性能的優化,怎么做到的?如果我繼承了List對性能會有什么影響?到底帶來了什么?
另外,List是微軟提供的,我不能控制它,當它對外界表征為“public API”我就不能修改它。但是我很難理解什么是Public API,為什么我要關心它?如果我的項目不需要或者根本沒有這樣的public API,我是不是就可以無視那個提示?如果我不繼承List那么我便需要一個Public API,我該怎么解決?為什么它這么重要呢,一個list就是個list,有什么可能改變的呢?我可能想要改變什么呢?
最后,如果微軟不希望我繼承List,為什么不給List類加sealed修飾符呢?
除此之外我該使用什么?
顯然,對于自定義的集合,微軟提供了可以繼承的Collection類來取代List。但是這個類功能很少,沒有提供很多有用的東西,比如說AddRange。
繼承Collection比繼承List要多做很多活,而且我找不到優點。微軟一定不會告訴我去做額外的活,所以我覺得某種程度上我誤解了什么,而且實際上對于我的問題繼承Collection也不是一個好的解決方案。
我也看到一些解決方案,比如實現IList。但是我不會這么做,幾十行的范例代碼讓我覺得什么用處也沒有。
最終,一些人建議我這么使用List:
class FootballTeam { public List<FootballPlayer> Players; }
但是這么做有兩個問題:
1. 它使我的代碼變得不必要的冗長。我一定要調用my_team.Players.Count而不是my_team.Count。感激的是,用C#我可以定義索引器來創建索引,然后傳送List中的所有內部方法。但是我要寫很多代碼。做那么多的活我得到了什么?
2. 這只是抱怨沒有別的意思。一個足球隊不是包含一組運動員,它就是一組運動員。你不會說John McFootballer已經加入了某個球隊的運動員集合里,你會說John McFootballer加入了某個球隊。你不會說把一個字母加入到一串字符串的字母集合里,你會說把一個字符加入一串字符串里。你不會說把一本書放到一個圖書館的圖書集合里,你會說把一本書放到一個圖書館里。
我開始意識到為什么會這樣了,其實可以說成“把x添加到y的內部集合里”,但是這樣的方式思考世界似乎是違背常理的。
我的問題(總結)
用C#表達邏輯(也就是說,從人類的思考方式)是list之類的帶有鐘聲和口哨的數據結構,正確的方法是什么?
是不是繼承List<T>一直不會被接受?或者說什么時候可以?為什么能或不能?當決定是否繼承List<T>,一個程序員需要思考什么?
最佳答案:(來自 Eric Lippert,他是微軟的一名資深軟件設計工程師,之前伯樂在線翻譯過他的一些博文)
已經有很多很好的答案,我很樂意在他們的基礎上補充幾點。
用C#表達邏輯(也就說說,從人類的思考方式)是list之類的帶有鐘聲和口哨的數據結構,正確的方法是什么?
讓任意十個非計算機程序員并且熟悉足球的人來填寫下面這句話的空白處:
一個足球隊是一種特定的__________
有任何一個人寫“帶有鐘聲和口哨的運動員集合”嗎,或者還是都寫“運動隊”、“俱樂部”、“組織”?足球隊是一種特定的運動員集合這個概念只是你大腦中的想法并且也只是你而已。
List<T>是一種機制。足球隊是一種商業對象——也就是說,在程序中,這種對象是在業務邏輯領域中的一種概念。不要混淆了這些。足球隊就是一個團隊,它有一個花名冊,花名冊是一組運動員集合。它就是運動員的集合。所以定義一個屬性叫Roster表示List<Player>,并且當你使用時,定義成
ReadOnly List<Player>
除非你認為每個人都可以得到球隊的花名冊并且可以從中刪除隊員。
是不是繼承List<T>一直不會被接受?
不能被誰接受?我嗎?不是。
什么時候可以?
當你需要創建一種需要繼承List<T>機制的機制的時候。
當決定是否繼承List<T>,一個程序員需要思考什么?
我是在創建一種機制還是創建一個業務對象?
我要寫很多代碼。做那么多的活我得到了什么?
你花了大量的時間把你的問題敲打出來,這個時間已經夠你把與List<T>相關聯的成員方法寫上50遍以上了。顯然你是不怕冗長的,我們在這里討論的只是幾行代碼,這是幾分鐘的工作。
更新
我又思考了一下,還有一個理由來說明足球隊不是運動員的集合。事實上,覺得足球隊包含一個運動員集合也許也是一個不對的想法。不管是球隊是運動員集合還是有一個運動員集合,問題是你得到的只是球隊某個時刻的一個快照,我不知道你定義這個類的業務情景式什么,但是如果讓我來定義一個表示足球隊的類,我會先問自己,“多少西雅圖海鷹隊的隊員由于傷病錯過了2003~2013年之間的比賽 ?”或“哪一個先前在另一個隊效力的丹佛隊運動員擁有在碼跑中的最高年增長率?”或“今年Piggers 還會一路走好嗎?”
也就是說,對我來說一個足球隊就是一個對球員們雇傭、傷病、退役等生涯進行記錄的記錄集。很明顯,當前的花名冊就是一個重要的記錄,所以應該是首先要考慮的,但是對足球隊這個對象來說,也許你有更多別的你感興趣東西,但那需要有更高的歷史視角。
原文鏈接: stackoverflow 翻譯: 伯樂在線 - 閃了腰的企鵝
譯文鏈接: http://blog.jobbole.com/62371/