次の方法で共有


コレクションのガイドライン

このコンテンツは、 フレームワーク設計ガイドライン (再利用可能な .NET ライブラリの規則、イディオム、パターン、第 2 版) から、Pearson Education, Inc. のアクセス許可によって再印刷されます。 そのエディションは2008年に出版され、その後 、本は第3版で完全に改訂されています。 このページの情報の一部が古くなっている可能性があります。

一般的な特性を持つオブジェクトのグループを操作するように特別に設計された任意の型は、コレクションと見なすことができます。 ほとんどの場合、このような型が IEnumerable または IEnumerable<T>を実装するのに適しているため、このセクションでは、これらのインターフェイスの一方または両方を実装する型のみをコレクションと見なします。

❌ パブリック API で弱く型指定されたコレクションを使用しないでください。

すべての戻り値の型とコレクション項目を表すパラメーターは、その基本型ではなく、正確な項目の型である必要があります (これはコレクションのパブリック メンバーにのみ適用されます)。

❌ パブリック API で ArrayList または List<T> を使用しないでください。

これらの型は、パブリック API ではなく、内部実装で使用するように設計されたデータ構造です。 List<T> は、API のクリーンさと柔軟性を犠牲にして、パフォーマンスとパワーに最適化されています。 たとえば、 List<T>を返した場合、クライアント コードがコレクションを変更したときに通知を受け取ることができません。 また、 List<T> では、多くのシナリオでは役に立たない、または適用できない多くのメンバー ( BinarySearch など) が公開されます。 次の 2 つのセクションでは、パブリック API で使用するために特別に設計された型 (抽象化) について説明します。

❌ パブリック API で Hashtable または Dictionary<TKey,TValue> を使用しないでください。

これらの型は、内部実装で使用するように設計されたデータ構造です。 パブリック API では、インターフェイスの一方または両方を実装する IDictionaryIDictionary <TKey, TValue>、またはカスタム型を使用する必要があります。

IEnumerator<T> メソッドの戻り値の型を除き、これらのインターフェイスのいずれかを実装するIEnumeratorGetEnumerator、またはその他の型は使用しないでください。

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> のサブクラスを返すかどうかを検討してください。

これにより、ヘルパー メソッドを追加したり、将来コレクションの実装を変更したりできるようになります。

✔️ コレクションに格納されている項目に一意のキー (名前、ID など) がある場合は、キー付きコレクションの使用を検討してください。 キー付きコレクションは、整数とキーの両方でインデックスを作成でき、通常は KeyedCollection<TKey,TItem>から継承して実装されるコレクションです。

キー付きコレクションは通常、メモリ占有領域が大きいため、メモリ オーバーヘッドがキーを持つ利点を上回る場合は使用しないでください。

❌ コレクション プロパティまたはコレクションを返すメソッドから null 値を返さないでください。 代わりに、空のコレクションまたは空の配列を返します。

一般的な規則では、null と空 (0 項目) のコレクションまたは配列は同じように扱う必要があります。

スナップショットとライブ コレクション

ある時点の状態を表すコレクションは、スナップショット コレクションと呼ばれます。 たとえば、データベース クエリから返される行を含むコレクションはスナップショットになります。 常に現在の状態を表すコレクションは、ライブ コレクションと呼ばれます。 たとえば、 ComboBox 項目のコレクションはライブ コレクションです。

❌ プロパティからスナップショット コレクションを返さないでください。 プロパティはリアルタイムに更新されるコレクションを返す必要があります。

プロパティゲッターは非常に軽量な操作であることが求められます。 スナップショットを返す場合は、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 パターンに可能な限り密接に従ってください。 つまり、同じメンバーを明示的に実装し、これら 2 つのコレクションのようなパラメーターに名前を付けます。

✔️ コレクションが多くの場合、これらのインターフェイスを入力として受け取る API に渡される場合は、非ジェネリック コレクション インターフェイス (IListICollection) を実装することを検討してください。

❌ コレクションの概念とは無関係の複雑な API を持つ型にコレクション インターフェイスを実装しないようにします。

CollectionBaseなどの非ジェネリック基本コレクションから継承しないでください。 代わりに、 Collection<T>ReadOnlyCollection<T>、および KeyedCollection<TKey,TItem> を使用します。

カスタム コレクションの名前付け

コレクション ( IEnumerableを実装する型) は主に 2 つの理由で作成されます。(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. 無断転載を禁じます。

フレームワーク設計ガイドライン:再利用可能な .NET ライブラリの規則、イディオム、パターン、Krzysztof Cwalina および Brad Abrams による第 2 版は、2008 年 10 月 22 日に Microsoft Windows 開発シリーズの一部として Addison-Wesley Professional によって公開されました。

こちらも参照ください