web-dev-qa-db-ja.com

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

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

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を使用します

これをどのように修正する必要があり、ここでの良い習慣は何ですか?

150
bovium

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

将来のプルーフ

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

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

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

APIシンプル

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

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

217
Greg Beech

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

48
Jon Skeet

まだ誰が「なぜ」の部分に答えているとは思わない...だからここに行く。 「なぜ」「使うべき」の理由はCollection<T>の代わりにList<T>は、List<T>を使用すると、オブジェクトにアクセスできる人は誰でもリスト内のアイテムを変更できます。一方、Collection<T>は、独自の「追加」、「削除」などのメソッドを作成していることを示すことになっています。

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

パブリック配列がある場合、例:

public int[] MyIntegers { get; }

誰も値を変更できない「get」アクセサーしか存在しないためだと思うでしょうが、それは真実ではありません。誰でも次のように内部の値を変更できます。

someObject.MyIngegers[3] = 12345;

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

2
Timothy Khouri

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

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

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

どのように修正するかは、クラスに何をさせたいか、GetList()メソッドの目的によって異なります。詳しく説明してもらえますか?

1
chakrit

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

1

これは尋ねられてから長い時間ですが、追加するものがあります。

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

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

1
NullReference

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

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

切断された内部データのcopyを返した場合、またはデータクエリの分離された結果-実装の詳細を公開せずにList<TItem>を安全に返し、返されたデータを便利な方法。

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

0