集合方針
注意
此內容是由 Pearson Education, Inc. 授權轉載自架構設計指導方針:可重複使用 .NET 程式庫的慣例、慣用語和模式,第 2 版。 該版於 2008 年出版,該書自那以後已於第三版進行了全面修訂。 此頁面上的某些資訊可能已過期。
任何特別設計用來操作具有一些通用特性之物件群組的型別,都可以視為集合。 這幾乎一律適用於這類型別實作 IEnumerable 或 IEnumerable<T>,因此在本節中,我們只會考慮實作其中一個或兩個介面的型別成為集合。
❌ 請勿在公用 API 中使用弱式型別集合。
代表集合項目的所有傳回值和參數型別都應該是確切的項目類型,而不是其任何基底類型 (這只適用於集合的公用成員)。
❌ 請勿在公用 API 中使用 ArrayList 或 List<T>。
這些型別是設計成用於內部實作的資料結構,而不是在公用 API 中使用。 List<T>
針對效能和電源最佳化,代價是 API 和彈性的清理。 例如,如果您傳回 List<T>
,當用戶端程式代碼修改集合時,您無法接收通知。 此外,List<T>
也會公開許多成員,例如 BinarySearch,在許多案例中都不實用或不適用。 下列兩節說明專為在公用 API 中使用而設計的型別 (抽象)。
❌ 請勿在公用 API 中使用 Hashtable
或 Dictionary<TKey,TValue>
。
這些型別是設計成用於內部實作的資料結構。 公用 API 應該使用 IDictionary、IDictionary <TKey, TValue>
,或實作一個或兩個介面的自訂型別。
❌ 請勿使用 IEnumerator<T>、IEnumerator,或實作任一介面的其他任何型別,除非是 GetEnumerator
方法的傳回型別。
從 GetEnumerator
以外的方法傳回列舉值的型別,不能與 foreach
陳述式搭配使用。
❌ 請勿在相同型別上實作 IEnumerator<T>
和 IEnumerable<T>
。 這同樣適用於非泛型介面 IEnumerator
和 IEnumerable
。
集合參數
✔️ 請盡可能使用最小特製化型別作為參數型別。 大部分採用集合作為參數的成員都會使用 IEnumerable<T>
介面。
❌ 請避免使用 ICollection<T> 或 ICollection 作為只是用來存取 Count
屬性的參數。
請考慮改用 IEnumerable<T>
或 IEnumerable
,並動態檢查物件是否實作 ICollection<T>
或 ICollection
。
集合屬性和傳回值
❌ 請勿提供可設定的集合屬性。
使用者可以先清除集合,然後新增新內容,以取代集合的內容。 如果取代整個集合是常見的案例,請考慮在集合上提供 AddRange
方法。
✔️ 針對代表讀取/寫入集合的屬性或傳回值,請使用 Collection<T>
或 Collection<T>
的子類別。
如果 Collection<T>
不符合某些需求 (例如,集合不得實作 IList),請實作 IEnumerable<T>
、ICollection<T>
或 IList<T> 來使用自訂集合。
✔️ 針對代表唯讀集合的屬性或傳回值,使用 ReadOnlyCollection<T>、ReadOnlyCollection<T>
的子類別,或在罕見的情況下使用 IEnumerable<T>
。
一般而言,偏好使用 ReadOnlyCollection<T>
。 如果不符合某些需求 (例如,集合不得實作 IList
),請實作 IEnumerable<T>
、ICollection<T>
或 IList<T>
來使用自訂集合。 如果您實作自訂唯讀集合,請實作 ICollection<T>.IsReadOnly
以傳回 true
。
如果您確定唯一想要支援的案例是順向反覆項目,您可以直接使用 IEnumerable<T>
。
✔️ 請考慮使用泛型基底集合的子類別,而不是直接使用集合。
這可提供更好的名稱,以及新增不存在於基底集合型別的協助程式成員。 這特別適用於高階 API。
✔️ 請考慮從非常常用的方法和屬性傳回 Collection<T>
或 ReadOnlyCollection<T>
的子類別。
這可讓您新增協助程式方法,或在未來變更集合實作。
✔️ 如果儲存在集合中的項目具有唯一索引鍵 (名稱、識別碼等),請考慮使用索引鍵集合。 索引鍵集合是可由整數和索引鍵編製索引的集合,通常是藉由繼承自 KeyedCollection<TKey,TItem>
來實作。
索引鍵集合通常具有較大的記憶體使用量,而且如果記憶體額外負荷超過擁有索引鍵的優點,就不應該使用。
❌ 請勿從集合屬性或從傳回集合的方法傳回 Null 值。 請改為傳回空集合或空陣列。
一般規則是應該將 Null 和空白 (0 個項目) 集合或陣列視為相同。
快照集與即時集合
代表某個時間點狀態的集合稱為快照集集合。 例如,包含從資料庫查詢傳回之資料列的集合會是快照集。 一律代表目前狀態的集合稱為即時集合。 例如,ComboBox
項目的集合是即時集合。
❌ 請勿從屬性傳回快照集集合。 屬性應該會傳回即時集合。
屬性 getter 應該是非常輕量的作業。 傳回快照集需要在 O(n) 作業中建立內部集合的複本。
✔️ 請務必使用快照集集合或即時 IEnumerable<T>
(或其子型別) 來代表動態集合 (亦即可以變更而不明確修改集合)。
一般而言,代表共用資源的所有集合 (例如目錄中的檔案) 都是動態。 除非實作只是順向列舉程式,否則這類集合很難或無法實作為即時集合。
在陣列和集合之間選擇
✔️ 偏好使用集合而非陣列。
集合提供對內容的更多控制權,可隨著時間演進,而且更容易使用。 此外,不建議針對唯讀案例使用陣列,因為複製陣列的成本是禁止的。 可用性研究顯示,有些開發人員更熟悉使用以集合為基礎的 API。
不過,如果您要開發低階 API,最好是針對讀寫案例使用陣列。 陣列的記憶體使用量較小,有助於減少工作集,而且陣列中的元素存取速度較快,因為已由執行階段最佳化。
✔️ 請考慮在低階 API 中使用陣列,以將記憶體耗用量降至最低,並將效能最大化。
✔️ 請使用位元組陣列,而不是位元組集合。
❌ 如果每次呼叫屬性 getter 時,屬性必須傳回新的陣列 (例如內部陣列的複本),請勿使用屬性的陣列。
實作自訂集合
✔️ 請考慮在設計新集合時繼承自 Collection<T>
、ReadOnlyCollection<T>
或 KeyedCollection<TKey,TItem>
。
✔️ 設計新集合時,請實作 IEnumerable<T>
。 請考慮實作 ICollection<T>
或甚至是 IList<T>
(如果合理的話)。
實作這類自訂集合時,請盡可能遵循 Collection<T>
和 ReadOnlyCollection<T>
所建立的 API 模式。 也就是說,明確實作相同的成員、將參數命名為類似這兩個集合的名稱等等。
✔️ 如果集合通常會傳遞至採用這些介面作為輸入的 API,請考慮實作非泛型集合介面 (IList
和 ICollection
)。
❌ 請避免在具有與集合概念無關之複雜 API 的型別上,實作集合介面。
❌ 請勿從非泛型基底集合繼承,例如 CollectionBase
。 請改用 Collection<T>
、ReadOnlyCollection<T>
和 KeyedCollection<TKey,TItem>
。
命名自訂集合
集合 (實作 IEnumerable
的型別) 主要針對以下兩個原因建立:(1) 建立新的資料結構,具有結構特定作業且通常有與現有資料結構不同的效能特性 (例如,List<T>、LinkedList<T>、Stack<T>),以及 (2) 建立特殊化集合來保存一組特定項目 (例如,StringCollection)。 資料結構最常用於應用程式和程式庫的內部實作。 特殊化集合主要是在 API 中公開 (作為屬性和參數型別)。
✔️ 請務必在實作 IDictionary
或 IDictionary<TKey,TValue>
的抽象概念名稱中使用 "Dictionary" 尾碼。
✔️ 請務必在實作 IEnumerable
(或其任何子系) 並代表項目清單的型別名稱中,使用 "Collection" 尾碼。
✔️ 請對自訂資料結構使用適當的資料結構名稱。
❌ 請避免在集合抽象名稱中,使用會表示特定實作的任何尾碼,例如 "LinkedList" 或 "Hashtable"。
✔️ 請考慮在集合名稱前面加上項目類型的名稱。 例如,儲存型別為 Address
(實作 IEnumerable<Address>
) 之項目的集合應該名為 AddressCollection
。 如果項目類型是介面,則可以省略項目類型的 "I" 前置詞。 因此,IDisposable 項目的集合可以稱為 DisposableCollection
。
✔️ 如果架構中可能加入或已經存在對應的可寫入集合,請考慮在唯讀集合的名稱中使用 "ReadOnly" 前置詞。
例如,字串的唯讀集合應該稱為 ReadOnlyStringCollection
。
Portions © 2005, 2009 Microsoft Corporation. 著作權所有,並保留一切權利。
獲 Pearson Education, Inc. 的授權再版,從 Krzysztof Cwalina 和 Brad Abrams 撰寫,並在 2008 年 10 月 22 日由 Addison-Wesley Professional 出版,作為 Microsoft Windows Development Series 一部份的 Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, 2nd Edition 節錄。