web-dev-qa-db-ja.com

ジェネリックICollectionが.NET 4.5でIReadOnlyCollectionを実装しないのはなぜですか?

.NET 4.5/C#5では、IReadOnlyCollection<T>Countプロパティを使用して宣言されます。

public interface IReadOnlyCollection<out T> : IEnumerable<T>, IEnumerable
{
    int Count { get; }
}

ICollection<T>IReadOnlyCollection<T>インターフェースを実装するのは理にかなっているのではないでしょうか。

public interface ICollection<T> : IEnumerable<T>, IEnumerable, *IReadOnlyCollection<T>*

これは、ICollection<T>を実装するクラスがIReadOnlyCollection<T>を自動的に実装することを意味します。これは私には合理的に聞こえます。

ICollection<T>抽象化は、IReadOnlyCollection<T>抽象化の拡張と見なすことができます。たとえば、List<T>ICollection<T>IReadOnlyCollection<T>の両方を実装することに注意してください。

ただし、そのようには設計されていません。

ここで何が欠けていますか?なぜ現在の実装が代わりに選択されたのですか?


[〜#〜]更新[〜#〜]

オブジェクト指向設計推論を使用して理由を説明する答えを探しています:

  • List<T>などの具象クラスは、IReadOnlyCollection<T>の両方を実装しますICollection<T>

は次のデザインより優れています:

  • ICollection<T>実装IReadOnlyCollection<T>を直接

また、これは基本的に次の質問と同じです。

  1. IList<T>IReadOnlyList<T>を実装しないのはなぜですか?
  2. IDictionary<T>IReadOnlyDictionary<T>を実装しないのはなぜですか?
54
Zaid Masud

ジョンはここにいた https://stackoverflow.com/a/12622784/395144 、彼の返信を回答としてマークする必要があります:

int ICollection<Foo>.Count { ... } // compiler error!

インターフェースは明示的な実装を持つことができるため、基本インターフェースの抽出は下位互換性がありません(基本クラスではこの問題はありません)。

それが理由です...

Collection<T> : IReadOnlyCollection<T>
List<T> : IReadOnlyList<T>
Dictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>

...しかし、それらのインターフェースではありません。

私見、彼らは最初は設計エラーを起こしましたが、今は(物事を壊すことなく)かなり解決できません。

編集:隠蔽は役に立ちません、古い(明示的な)実装はまだビルドされません(コードを変更しないと):

interface INew<out T> { T Get(); }

interface IOld<T> : INew<T>
{
    void Set(T value);
    new T Get();
}

class Old<T> : IOld<T>
{
    T IOld<T>.Get() { return default(T); }
    void IOld<T>.Set(T value) { }
}

「Sample.Old」はインターフェースメンバー「Sample.INew.Get()」を実装していません

14
Notoriousxl

おそらくいくつかの理由があります。ここに幾つかあります:

  • 大きな後方互換性の問題

    ICollection<T>の定義をどのように書きますか?これは自然に見えます:

    interface ICollection<T> : IReadOnlyCollection<T>
    {
        int Count { get; }
    }
    

    ただし、IReadOnlyCollection<T>Countプロパティも宣言しているため、問題があります(コンパイラはここで警告を発行します)。警告とは別に、そのままにすることで(new int Countを書くのと同じ)、実装者は2つのCountプロパティに対して異なる実装を持つことができます。少なくとも1つを明示的に実装する。 2つの実装が異なる値を返すことにした場合、これは「おもしろい」かもしれません。人々が自分の足で撃つことを許可するのは、C#のスタイルではありません。

    OK、それでどうですか:

    interface ICollection<T> : IReadOnlyCollection<T>
    {
        // Count is "inherited" from IReadOnlyCollection<T>
    }
    

    まあ、これはCountを明示的に実装することにした既存のすべてのコードを壊します:

    class UnluckyClass : ICollection<Foo>
    {
         int ICollection<Foo>.Count { ... } // compiler error!
    }
    

    したがって、この問題に対する適切な解決策はないようです:既存のコードを壊すか、everyoneにエラーが発生しやすい実装を強制します。つまり 勝つ唯一の手はプレーしないこと です。

36
Jon

明らかに、すべてのICollectionが読み取り専用であるとは限らないため、意味的に間違っています

とはいえ、それらはインターフェイスIReadableCollectionを呼び出すことができ、実装ReadOnlyCollectionを呼び出すことができます。

しかし、彼らはその道を進みませんでした。どうして?私は BCLチームメンバーが、コレクションAPIが複雑になりすぎないようにしたいと書いています を見ました。 (率直に言って、すでにそうですが。)

2
herzmeister