Collection <T>とList <T>では、インターフェイスで何を使用する必要がありますか?


162

コードは次のようになります。

namespace Test
{
    public interface IMyClass
    {
        List<IMyClass> GetList();
    }

    public class MyClass : IMyClass
    {
        public List<IMyClass> GetList()
        {
            return new List<IMyClass>();
        }
    }
}

コード分​​析を実行すると、次の推奨事項が表示されます。

警告3 CA1002:Microsoft.Design: 'IMyClass.GetList()'の 'List'を、Collection、ReadOnlyCollection、またはKeyedCollectionを使用するように変更します

これをどのように修正すればよいですか?ここでの良い習慣は何ですか?

回答:


234

「なぜ」ではないのかという質問の「なぜ」の部分に答えるためにList<T>、その理由は、将来性とAPIのシンプルさです。

将来を見据えた

List<T>サブクラス化することで簡単に拡張できるようには設計されていません。内部実装のために高速になるように設計されています。その上のメソッドは仮想ではないのでオーバーライドできないことに気づくでしょう。そのAdd/ Insert/ Remove操作へのフックはありません。

これは、将来的にコレクションの動作を変更する必要がある場合(たとえば、ユーザーが追加しようとするnullオブジェクトを拒否する場合、またはこれが発生したときにクラスの状態を更新するなどの追加の作業を実行する場合)、型を変更する必要があることを意味しますサブクラス化できるコレクションに戻るコレクションの場合、これはインターフェースの重大な変更になります(もちろん、nullを許可しないなどのセマンティクスを変更すると、インターフェースも変更される可能性がありますが、内部クラスの状態の更新などは行われません)。

だから、どちらかの簡単のようなサブクラス化することができるクラス返すことによって、Collection<T>またはインタフェースのようなIList<T>ICollection<T>またはIEnumerable<T>あなたはそれがまだとして返すことができるので、消費者のコードを壊すことなく、あなたのニーズを満たすためにさまざまなコレクション型であるためにあなたの内部実装を変更することができますが彼らが期待しているタイプ。

APIのシンプルさ

List<T>以下のような便利な操作がたくさん含まれているBinarySearchSortなどを。ただし、これが公開しているコレクションの場合は、コンシューマーではなくリストのセマンティクスを制御している可能性があります。したがって、クラスが内部でこれらの操作を必要とする可能性がある一方で、クラスのコンシューマがそれらを呼び出したい(または呼び出す必要がある)可能性はほとんどありません。

そのため、より単純なコレクションクラスまたはインターフェイスを提供することで、APIのユーザーに表示されるメンバーの数を減らし、ユーザーが使いやすくします。


1
この反応は完全に未解決です。主題のもう一つの良い読み取りがここで見つけることができます:blogs.msdn.com/fxcop/archive/2006/04/27/...
senfo

7
最初のポイントはわかりましたが、APIのシンプルさの部分に同意するかどうかはわかりません。
Boris Callens

stackoverflow.com/a/398988/2632991コレクションとリストの違いについての、本当に良い投稿もここにあります。
El Mac

2
blogs.msdn.com/fxcop/archive/2006/04/27/…- >死んでいます。これに関する新しい情報はありますか?
マチャド


50

個人的には、具体的なコレクションではなくインターフェイスを返すように宣言します。リストへのアクセスが本当に必要な場合は、を使用してくださいIList<T>。それ以外の場合は、ICollection<T>およびを検討してくださいIEnumerable<T>


1
IListはICollectionインターフェイスを拡張しますか?
ボビウム2008年

8
@ジョン:私はこれが古いことを知っていますが、Krzysztofがblogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspxで言っていることについてコメントできますか?特に彼のコメントであるWe recommend using Collection<T>, ReadOnlyCollection<T>, or KeyedCollection<TKey,TItem> for outputs and properties and interfaces IEnumerable<T>, ICollection<T>, IList<T> for inputs. CA1002は、Krzysztofのコメントに沿っているようです。なぜインターフェースの代わりに具体的なコレクションが推奨されるのか、なぜ入力と出力の区別がなされるのか、想像もできません。
Nelson Rothermel

3
@ネルソン:呼び出し元に不変のリストを渡すことを要求することはまれですが、リストを返して、確実に不変であることがわかるようにするのが妥当です。他のコレクションについてはわかりません。詳細を教えてください。
Jon Skeet、

2
特定のケースではありません。明らかに、一般的にReadOnlyCollection<T>入力には意味がありません。同様にIList<T>、入力が言うように、「Isort()またはIListが持っている他のメンバーが必要です」これは出力には意味がありません。しかし、私が意味したのは、なぜICollection<T>入力およびCollection<T>出力として推奨されるのかということです。あなたが提案したように、出力としても使用しないのはなぜICollection<T>ですか?
Nelson Rothermel

9
それは曖昧さには関係ないと思います。 Collection<T>およびReadOnlyCollection<T>両方から派生しますICollection<T>(つまり、はありませんIReadOnlyCollection<T>)。インターフェースを返す場合、それがどのインターフェースであり、変更できるかどうかは明らかではありません。とにかく、あなたの入力をありがとう。これは私にとって良い健全性チェックでした。
Nelson Rothermel

3

「なぜ」の部分にはまだ誰も答えていないと思います...それで、ここに行きます。のCollection<T>代わりにを使用する必要List<T>がある理由は、を公開するList<T>と、オブジェクトへのアクセス権を持つ誰もがリスト内のアイテムを変更できるためです。一方Collection<T>、独自の「追加」、「削除」などのメソッドを作成していることを示しているはずです。

あなたはおそらく自分だけ(またはおそらくいくつかの同僚)のためにインターフェイスをコーディングしているので、心配する必要はありません。意味のある別の例を次に示します。

公開配列がある場合、例:

public int[] MyIntegers { get; }

だれも値をいじることができない "get"アクセサーしかないので、あなたはそれを考えますが、それは真実ではありません。誰でもこのように内部の値を変更できます:

someObject.MyIngegers[3] = 12345;

個人的にはList<T>、ほとんどの場合に使用します。しかし、ランダムな開発者に配布するクラスライブラリを設計していて、オブジェクトの状態に依存する必要がある場合は、独自のコレクションを作成し、そこからロックする必要があります。 )


「List <T>をクライアントコードに返すと、クライアントコードがコレクションを変更したときに通知を受け取ることができなくなります。」-FX COP ...「msdn.microsoft.com/en-us/library/0fss9skc.aspx」も参照してください ...うわー、結局私はベースから外れていないようです:)
Timothy Khouri

うわー-数年後、上記のコメントに貼り付けたリンクの最後に引用があったため、機能しませんでした... msdn.microsoft.com/en-us/library/0fss9skc.aspx
Timothyコーリー

2

それは主に、直接操作されるListオブジェクトを公開するのではなく、独自の実装を抽象化することです。

他のオブジェクト(または人々)がオブジェクトの状態を直接変更できるようにすることはお勧めできません。プロパティのゲッター/セッターを考えてください。

コレクション->通常のコレクションの場合
ReadOnlyCollection->変更してはならないコレクションの
場合KeyedCollection->代わりに辞書が必要な場合。

それを修正する方法は、クラスに何をさせたいか、およびGetList()メソッドの目的によって異なります。詳しく説明できますか?


1
ただし、Collection <T>とKeyedCollection <、>も読み取り専用ではありません。
nawfal 14

@nawfalそして、それはどこにあると私は言いましたか?
chakrit 14

1
あなたは状態他のオブジェクト(または人)が直接あなたのオブジェクトの状態を変更できないと、リスト3コレクション型。ReadOnlyCollection他の2つを除外すると、それに従いません。
nawfal 2014

それは「良い習慣」を意味しています。適切なコンテキストでお願いします。OPが警告を理解したいので、以下のリストは、そのようなタイプの基本的な要件を簡単に述べています。それから私はGetList()彼にもっと正確に彼を助けることができるようにする目的について彼に尋ね続けます。
chakrit 14

1
わかりました。その部分は理解しました。しかし、それでも推論の部分は私によれば健全ではありません。あなたCollection<T>は、内部実装を抽象化するのに役立ち、内部リストの直接操作を防ぎます。どうやって?Collection<T>渡された同じインスタンスで動作する単なるラッパーです。これは継承を目的とした動的なコレクションであり、他には何もありません(Gregの答えはここでより関連しています)。
nawfal 14

1

このような場合、私は通常、必要最小限の実装を公開しようとします。実際にリストを使用していることを消費者が知る必要がない場合は、リストを返す必要はありません。Microsoftがコレクションを提案するように戻ることで、リストを使用しているという事実をクラスのコンシューマーから隠し、内部の変更からそれらを分離します。


1

これが尋ねられてから久しぶりですが、何か追加する必要があります。

リストタイプがのList<T>代わりにから派生する場合、Collection<T>実装する保護された仮想メソッドをCollection<T>実装することはできません。つまり、リストに変更が加えられた場合、派生型は応答できません。これは、List<T>アイテムを追加または削除するときに知っていると想定しているためです。通知に応答できることはオーバーヘッドであり、それをList<T>提供しません。

外部コードがコレクションにアクセスできる場合、アイテムがいつ追加または削除されるかを制御できない場合があります。したがってCollection<T>、リストがいつ変更されたかを知る方法を提供します。


0

次のようなものを返すことに問題はありません

this.InternalData.Filter(crteria).ToList();

内部データの切断されたコピー、またはデータクエリの分離された結果を返した場合 - List<TItem>実装の詳細を公開せずに安全に戻ることができ、返されたデータを便利な方法で使用できます。

しかし、これは私が期待する消費者のタイプに依存します-これがデータグリッドのようなものである場合、ほとんどの場合、とにかくIEnumerable<TItem> アイテムのコピーされたリストである返却たいです:)


-1

まあ、Collectionクラスは、実際には他のコレクションのラッパークラスにすぎず、実装の詳細やその他の機能を隠しています。これは、オブジェクト指向言語のプロパティ非表示コーディングパターンと関係があると思います。

心配する必要はないと思いますが、コード分析ツールに本当に満足したい場合は、次のようにしてください。

//using System.Collections.ObjectModel;

Collection<MyClass> myCollection = new Collection<MyClass>(myList);

1
すみません、それはタイプミスでした。Collection <MyClass>を作成します。C#4と一般的な共分散を手に入れられることを本当に楽しみにしています!
Tamas Czinege 2008年

Collection<T>私が理解している限り、a をラップしても、それ自体や基になるコレクションは保護されません。
nawfal 14

このコードは、「コード分析ツールをご利用ください」というものでした。@TamasCzinegeを使用してCollection<T>も、基になるコレクションがすぐに保護されるとは思いません。
chakrit 14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.