共用方式為


集合的指導方針

備註

此內容經Pearson Education, Inc.授權從架構設計指導方針:可重複使用 .NET 程式庫的慣例、習慣用語與範式 (第2版)轉載。 該版於2008年出版,該書自那以後已於 第三版全面修訂。 此頁面的某些資訊可能已過期。

專為操作具共同特性的一組物件而設計的任何類型都可以視為集合。 實作 IEnumerableIEnumerable<T> 這類類型幾乎一律適用,因此在本節中,我們只會考慮那些實作其中一個或兩個介面的類型來作為集合。

❌ 請勿在公用 API 中使用弱型別集合。

代表集合項目的所有傳回值和參數類型應該是確切的項目類型,而不是其任何基底類型(這只適用於集合的公用成員)。

❌ 請勿在公用 API 中使用 ArrayListList<T>

這些類型是設計成用於內部實作的數據結構,而不是在公用 API 中使用。 List<T> 針對效能和性能進行優化,但以犧牲 API 的整潔性和靈活性為代價。 例如,如果您傳回 List<T>,則當用戶端程式代碼修改集合時,您將永遠無法接收通知。 此外,List<T> 也會公開許多成員,例如 BinarySearch,在許多情境下不具意義或不適用。 下列兩節描述專為在公用 API 中使用的類型(抽象概念)。

❌ 請勿在公用 API 中使用 HashtableDictionary<TKey,TValue>

這些類型是設計用於內部實作的數據結構。 公用 API 應該使用 IDictionaryIDictionary <TKey, TValue>或實作一或兩個介面的自定義型別。

❌ 請勿使用 IEnumerator<T>IEnumerator或任何其他實作這些介面的型別,但方法的 GetEnumerator 傳回型別除外。

無法將從非GetEnumerator方法返回列舉器的類型與foreach語句一起使用。

❌ 不要在相同類型上同時實作IEnumerator<T>IEnumerable<T>。 同樣適用於非泛型介面 IEnumeratorIEnumerable

集合參數

✔️ 請盡可能使用最不專門化的類型用作參數類型。 大部分採用集合做為參數的成員都會使用 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 的項目集合是動態集合。

❌ 請勿從屬性傳回快照集集合。 屬性應該會傳回動態集合。

屬性獲取器應該是非常輕量級的操作。 傳回快照需要在 O(n) 作業中建立一個內部集合的複本。

✔️ DO 使用快照集合或即時 IEnumerable<T> (或其子類型)來表示容易變動的集合(亦即可以在不明確修改集合的情況下變更的集合)。

一般而言,代表共用資源的所有集合(例如目錄中的檔案)都是不穩定的。 除非實作只是單向的列舉器,否則這類集合要作為動態集合實作是非常困難或不可能的。

在陣列和集合之間選擇

✔️ DO 偏好集合而不是陣列。

集合可提供對內容的更多控制、隨著時間演進,而且更容易使用。 此外,不建議針對唯讀情境使用陣列,因為複製陣列的成本是令人望而卻步的。 可用性研究顯示,有些開發人員使用以集合為基礎的 API 感覺更舒服。

不過,如果您正在開發低階 API,最好是針對讀寫場景使用陣列。 陣列的記憶體占用較小,有助於減少工作集大小,而且存取陣列中的元素速度較快,因為其已由執行時期優化。

✔️ 請考慮在低階 API 中使用陣列,以將記憶體耗用量降至最低,並將效能最大化。

✔️ 請使用位元組陣列,而非位元組集合。

❌ 請勿針對屬性使用陣列,若屬性每次呼叫屬性 getter 時都必須傳回新的陣列(例如內部陣列的複本)。

實作自定義集合

✔️ 請考慮在設計新集合時從 Collection<T>ReadOnlyCollection<T>KeyedCollection<TKey,TItem> 繼承。

✔️ 在設計新系列時,請實施 IEnumerable<T>。 請考慮在合適的地方實作 ICollection<T> 或甚至 IList<T>

實作這類自定義集合時,請盡可能遵循由 Collection<T>ReadOnlyCollection<T> 所建立的 API 模式。 也就是說,明確實作相同的成員,並將參數命名為類似這兩個集合的名稱,等等。

✔️ 如果集合通常會傳遞至採用這些介面做為輸入的 API,請考慮實作非泛型集合介面 (IListICollection) 。

❌ 避免在與集合概念無關的複雜 API 類型上實作集合介面。

❌ 請勿繼承自非泛型基底集合,例如 CollectionBase。 請改用 Collection<T>ReadOnlyCollection<T>KeyedCollection<TKey,TItem>

命名自訂集合

集合(IEnumerable的實作類型)主要是基於兩個原因而建立:(1)創建具備結構特定操作的新資料結構,且效能特性通常不同於現有的資料結構(例如,List<T>LinkedList<T>Stack<T>),以及(2)創建一個專門的集合來保存一組特定的專案(例如,StringCollection)。 數據結構最常用於應用程式和連結庫的內部實作中。 特製化集合主要是在 API 中公開(作為屬性和參數類型)。

✔️ 請在實作IDictionaryIDictionary<TKey,TValue>的抽象概念名稱中使用「Dictionary」後綴。

✔️ 請在實作 IEnumerable(或其任何子系)並表示項目清單的類型名稱中使用「Collection」後綴。

✔️ 請針對自訂數據結構使用適當的數據結構名稱。

❌ 請避免在集合抽象概念的名稱中使用任何隱含特定實作的後綴,例如 “LinkedList” 或 “Hashtable”。

✔️ 請考慮在集合名稱前面加上項目類型的名稱。 例如,儲存型別 Address(實作 IEnumerable<Address>)的集合專案應該命名為 AddressCollection。 如果專案類型是介面,則可以省略項目類型的 「I」 前置詞。 因此,一個由IDisposable項目組成的集合可以稱為DisposableCollection

✔️ 如果架構中可能會加入或已經存在對應的可寫入集合,請考慮在唯讀集合的名稱中使用 「ReadOnly」 前置詞。

例如,字串的唯讀集合應該稱為 ReadOnlyStringCollection

© 2005年、2009年Microsoft公司部分。 保留所有權利。

經 Pearson Education, Inc. 許可重新刊登自 Krzysztof Cwalina 和 Brad Abrams 所著的 架構設計指導方針: 可重複使用的 .NET 程式庫慣例、慣用語和模式,第 2 版,2008 年 10 月 22 日由 Addison-Wesley Professional 發行,作為 Microsoft Windows 開發系列的一部分。

另請參閱