21

ReadOnlyCollectionクラスの仕様を見ると、 IListインターフェイスが実装されています。

IList インターフェイスには Add/Update/Read メソッドがあり、これをインターフェイスの事前条件と呼びます。IList があれば、アプリケーションのどこでも、この種の操作をすべて実行できるはずです。

しかし、コードのどこかで ReadOnlyCollection を返し、.Add(...) メソッドを呼び出そうとしたらどうなるでしょうか。NotSupportedException をスローします。これは悪いデザインの良い例だと思いますか? さらに、このクラスはLiskov Substitution Principleを破っていますか?

Microsoft がこのように実装したのはなぜですか? この ReadOnlyCollection が IEnumerable インターフェイスのみを実装するようにする方が簡単 (かつより良い) でしょうか (ちなみに、これは既に読み取り専用です)。

4

8 に答える 8

9

はい、確かに悪い設計です。.NET にはコレクション インターフェイスがありません。読み取り専用インターフェイスはありません。

string[]その実装IList<string>(および他の型についても同様)をご存知ですか? Addこれには同じ問題があります。インターフェイスでandを呼び出すことができると予想されますがRemove、スローされます。

残念ながら、後方互換性を失わずにこれを変更することはできませんが、非常に悪い設計であることに同意します。より良い設計では、読み取り専用機能用の別のインターフェイスが表示されます。

于 2010-10-08T23:27:28.883 に答える
9

IList<T>インターフェイスはメソッドを定義Add(T)しますが、プロパティInsert(int,T)も定義します。MSDN のIList.Insert(int,T ) メソッドとIList.Add(T)メソッドの定義を注意深く読むと、リストが読み取り専用。 IsReadOnlyNotSupportedException

その理由で設計が悪いと言うのは、インデックスが負の場合、またはコレクションのサイズよりも大きい場合にInsert(int, T)スローできるため、設計も悪いと言うのと同じです。ArgumentOutOfRangeException

于 2010-10-08T23:37:05.520 に答える
5

それは素晴らしいデザインではありませんが、私の意見では必要悪です。

残念なことに、Microsoft は と のようなものIReadableList<>IWriteableList<>フレームワークに含めずIList<>、それらの両方を実装しました (またはIList<>完全にスキップしてIWriteableList<>実装しましたIReadableList<>)。問題が解決しました。

しかし、今変更するには遅すぎます。コレクションにリストのセマンティクスが必要な状況があり、ミューテーションを許可するのではなく、実行時に例外をスローしたい場合ReadOnlyCollection<>は、残念ながら最善の選択肢です。

于 2010-10-08T23:30:55.487 に答える
2

IList には、Item や IndexOf(..) などの読み取りメソッドとプロパティがあります。ReadOnlyCollection が IEnumerable のみを実装する場合、それらを見逃すことになります。


代替手段は何ですか?IList の読み取り専用バージョンと書き込みバージョンがありますか? これにより、BCL 全体が複雑になります (LINQ については話せません)。


また、サポートされていない例外をスローできることが (IList の) ベース レベルで定義されているため、Liskov Substitution Principle に違反しているとは思いません。

于 2010-10-08T23:22:39.033 に答える
2

抽象化と専門化のトレードオフの良い例だと思います。

IList の柔軟性が必要ですが、いくつかの制約も課したい場合はどうすればよいでしょうか? 設計方法は少しぎこちなく、技術的には設計原則に違反している可能性がありますが、同じ機能とシンプルさを提供するために何が優れているかはわかりません.

この場合、別の IListReadOnly インターフェイスを用意したほうがよい場合があります。ただし、1 回限りの使用インターフェイスが急増し、事態が非常に混乱するようになるのは簡単です。

于 2010-10-08T23:27:45.670 に答える
1

その悪い設計IMO。おそらく下位互換性の問題、共分散や反分散の欠落などによって強制された可能性があります。幸いなことに、.NET 4.5 で次のように対処しました。

IReadOnlyList<out T>
IReadOnlyCollection<out T>
IReadOnlyDictionary<TKey, TValue>

ただし、「bool Contains(T)」を使用した「読み取り専用」インターフェイスがありません。

于 2013-07-03T13:37:32.623 に答える
1

設計が悪いと言えます。機能クエリを備えた適度に大きなインターフェースの概念を受け入れたとしても、特定の動作が許可されることを保証する、それらから継承された他のインターフェースがあったはずです。例えば:

  1. IEnumerable (既存のものとよく似ていますが、リセットがなく、列挙中にコレクションが変更された場合に何が起こるかは保証されません)
  2. IMul​​tipassEnumerable (リセットを追加し、変化するコレクションの繰り返し列挙が同じデータを返すか、スローして例外を返すことを保証します)
  3. ICountableEnumerable (マルチパス列挙可能、「カウント」プロパティ、および列挙子とカウントを同時に取得するメソッド)
  4. IModifiableEnumerable (列挙を行うスレッドによって列挙中にコレクションが変更された場合にスローされない IEnumerator。正確な動作は指定されませんが、列挙中に変更されていないアイテムは 1 回だけ返される必要があります。列挙中に変更されたアイテムは、追加または変更ごとに最大で 1 回返され、列挙開始時にそれらが存在する場合は 1 回返されます (このインターフェイス自体はミューテーションを提供しませんが、ミューテーションを提供する他のインターフェースと組み合わせて使用​​されます)。
  5. ICopyableAsEnumerable (count プロパティと、リストのスナップショットを表す IEnumerable を返すメソッドを含みます。実際には IEnumerable 自体ではありませんが、IEnumerable が提供する便利な機能です)。
  6. IImmutable (メンバーはありませんが、保証された不変のインターフェイスを作成するために継承可能です)
  7. IImmutableEnumerable
  8. IImmutableCountableEnumerable
  9. IList (読み取り可能、読み書き可能、または不変)
  10. IImmutableList (新しいメンバーはありませんが、IImmutable を継承しています)
  11. IWritableList (新しいメンバーはありませんが、書き込み可能であることが保証されています)

これはほんの一例ですが、デザインのアイデアを伝える必要があります。

于 2010-12-04T04:41:25.970 に答える
-3

悪い設計が行われている場合、ReadOnly プロパティをチェックせずに IList に追加するのが習慣だと思います。インターフェイスの一部を無視するというプログラマーの習慣は、インターフェイスが貧弱であることを意味するわけではありません。

真実は、私たちプログラマーの中でわざわざ仕様書を読む人はほとんどいないということです。正直なところ、座って仕様書全体を読むよりもワクワクするようなことがたくさんあります。(たとえば、つまようじで本当に目を開けたままにできるかどうかを確認するようなものです。) それに、とにかくすべてを覚えているわけではないという制限があります。

そうは言っても、少なくともプロパティとメソッドのリストを見ずにインターフェイスを使用するべきではありません。そして、「ReadOnly」という名前のブール値のプロパティの目的は何だと思いますか? おそらく、リストが何らかの理由でしか読み取れないためです。また、自分のコード以外の場所から渡されたリストを取得する場合は、リストに追加する前に、リストが読み取り専用ではないことを確認する必要があります。

于 2010-10-09T22:06:47.310 に答える