なぜ配列はIListを実装するのですか?


141

System.Arrayクラスの定義を参照してください

public abstract class Array : IList, ...

理論的には、このビットを書いて幸せになれるはずです

int[] list = new int[] {};
IList iList = (IList)list;

また、iListから任意のメソッドを呼び出すことができるはずです。

 ilist.Add(1); //exception here

私の質問は、なぜ例外が発生するのではなく、なぜArrayがIListを実装するのかです。


22
いい質問だ。ファットインターフェイスのアイデアは好きではありませんでした(これがこの種のデザインの専門用語です)。
Konrad Rudolph、


2
誰かが実際にLSPを気にしていますか?それは私にはかなり学術的なようです。
Gabe

13
@Gabeの場合、より大きなコードベースで作業する必要があります。振る舞いを実装し(インターフェースから継承)、単純にあなたが好きでない/サポートできないものを無視すると、臭い、難読化、キャスト、そして最後にバグのあるコードにつながります。
マリウス

3
@Gabeそのコレクションは、含まれているエンティティではなく、可変性を意味します。IRWList <>とIReadList <>の両方を実装する型のクラスメンバーを作成し、クラスの内部でIRWList <>として使用し、それをIReadListとして公開できます。はい、複雑さをどこかに置く必要がありますが、LSPを非常に優れた設計原則として無視する場合にどのように適用されるかはわかりません(IsReadOnlyプロパティについては知りませんでしたが、IListがコンシューマーの観点からより複雑になります)
Marius

回答:


94

配列はインデックスによる高速アクセスを許可し、IList/ IList<T>はこれをサポートする唯一のコレクションインターフェイスであるためです。したがって、おそらくあなたの本当の質問は、「インデクサーを使用した定数コレクションのインターフェイスがないのはなぜですか?」そしてそれには答えがありません。

コレクションの読み取り専用インターフェースもありません。また、インデクサーインターフェイスを使用した一定のサイズ以上のものはありません。

IMOコレクションの機能に応じて、いくつかの(汎用)コレクションインターフェイスが必要です。Listインデクサー付きのものは本当に愚かなIMOであるため、名前も異なるはずです。

  • ただの列挙 IEnumerable<T>
  • 読み取り専用ですが、インデクサーはありません(.Count、.Contains、...)
  • サイズ変更可能ですが、インデクサーはありません。つまり、(Add、Remove、...)currentのように設定されています ICollection<T>
  • インデクサー付きの読み取り専用(インデクサー、indexof、...)
  • インデクサー付きの一定サイズ(セッター付きのインデクサー)
  • インデクサー付きの可変サイズ(挿入、...)現在 IList<T>

現在のコレクションインターフェイスはデザインが悪いと思います。しかし、それらには有効なメソッドを通知するプロパティがあるため(これはこれらのメソッドのコントラクトの一部です)、置換の原則に違反しません。


14
答えてくれてありがとう。しかし、私はむしろ質問をそのままにしておきます。理由は簡単です。インターフェイスは公的な契約です。それを実装する場合、すべてのメンバーを完全に実装する必要があります。そうしないと、LSPが壊れ、一般的に悪臭がしますか?
oleksii

16
それはLSPを壊します。もしそれがlist.Add(item)でなければ、具体的なタイプに関係なくリストに項目を追加する必要があります。例外的なケースを除いて。の配列の実装では、例外ではない場合に例外をスローしますが、それ自体は悪い習慣です
Rune FS

2
@smelch申し訳ありませんが、LSPが間違っています。配列は実装されていないためadd、その機能が必要な場合に代わることはできません。
Rune FS

7
私はそれがいることを認める技術的に LSPに違反していないので、唯一のドキュメントは、あなたがチェックしなければならないと述べIsFixedSize及びIsReadOnly性質、それは間違いなく違反DOは原則として掲載していない、教える少なくとも驚きの原則を。9つのメソッドのうち4つに例外をスローするだけの場合に、なぜインターフェースを実装するのですか?
Matthew

11
元の質問から時間が経ちました。しかし、現在.Net 4.5では、追加のインターフェイスIReadOnlyListおよびIReadOnlyCollectionがあります。
トビアス

43

のためのドキュメントの備考セクションはIList言う

IListはICollectionインターフェイスの子孫であり、すべての非ジェネリックリストの基本インターフェイスです。 IList実装は、読み取り専用、固定サイズ、可変サイズの3つのカテゴリに分類されます。読み取り専用のIListは変更できません。固定サイズのIListでは要素の追加や削除はできませんが、既存の要素の変更は可能です。可変サイズのIListは、要素の追加、削除、および変更を可能にします。

明らかに配列は固定サイズのカテゴリーに分類されるため、インターフェースの定義により、それは理にかなっています。


4
私はそれらが多くのインターフェースになっていたと思います。IListFixedSize、IListReadOnly ...
Magnus

9
ドキュメントの観点からは、これは実際に良い答えです。しかし、私にはそれはむしろハックのように見えます。クラスがすべてのメンバーを実装するためには、インターフェースは薄くてシンプルでなければなりません。
oleksii

1
@oleksii:同意します。インターフェースとランタイム例外は、最も洗練された組み合わせではありません。それを守るArrayために、Addメソッドを明示的に実装します。これにより、誤ってメソッドを呼び出すリスクが軽減されます。
ブライアンラスムッセン

その実装を作成するまでIList、変更追加/削除の両方を許可しません。その後、ドキュメントは正しくありません。:P
Timo

1
@Magnus-.Net 4.5では、追加のインターフェースIReadOnlyListおよびIReadOnlyCollectionがあります。
RBT 2017

17

すべてIListのが変更可能であるわけではなくIList.IsFixedSizeおよびを参照IList.IsReadOnly)、配列は確かに固定サイズのリストのように動作します。

あなたの質問が本当に「非ジェネリックインターフェースを実装する理由」である場合、答えはジェネリックが登場する前にこれらがあったことです。


10
@oleksii:いいえ、それはLSPを壊しません。インターフェイスIList 自体は変更できない可能性があることを通知するからです。それが実際に変更可能であることが保証されていて、配列がそれ以外のことを通知した場合、それは規則に違反します。
user541686

実際には、アレイは、一般的な場合のブレークLSPを行いIList<T>かつ非ジェネリックの場合には、それを破壊しないIListenterprisecraftsmanship.com/2014/11/22/...
ウラジミール

5

これは、読み取り専用のコレクションを処理する方法や、配列が読み取り専用であるかどうかが明確ではなかった時代から私たちが持っている遺産です。IListインターフェイスにはIsFixedSizeフラグとIsReadOnlyフラグがあります。IsReadOnlyフラグはコレクションをまったく変更できないことを意味し、IsFixedSizeはコレクションが変更を許可するがアイテムの追加または削除は許可しないことを意味します。

純4.5の時点で、そう、いくつかの「中間」のインターフェイスは、読み取り専用のコレクションで動作するように要求されていることが明らかになったIReadOnlyCollection<T>IReadOnlyList<T>紹介されました。

ここでは詳細を説明する偉大なブログ記事です:.NETでの読み取り専用のコレクション


0

IListインターフェイスの定義は、「インデックスによって個別にアクセスできるオブジェクトの非ジェネリックコレクションを表します。」です。配列はこの定義を完全に満たすため、インターフェイスを実装する必要があります。Add()メソッドの呼び出し時の例外は「System.NotSupportedException:コレクションは固定サイズでした」であり、配列が容量を動的に増やすことができないために発生しました その容量は、配列オブジェクトの作成時に定義されます。


0

IEnumerableをIList / ICollectionにキャストすると配列でも機能するため、配列にIList(および推移的にICollection)を実装すると、Linq2Objectsエンジンが簡素化されました。

たとえば、Count()は、ICollectionにキャストされ、配列の実装がLengthを返すため、内部でArray.Lengthを呼び出すことになります。

これがなければ、Linq2Objectsエンジンは配列に対して特別な処理を行わず、恐ろしく実行するか、または(IListの場合と同様に)配列に対して特別なケースの処理を追加するコードを2倍にする必要があります。代わりに、配列にIListを実装させることを選択したに違いありません。

それが「なぜ」の私の見解です。


0

また、実装の詳細はLINQ LastがIListをチェックするため、リストを実装しなかった場合は、2つのチェックですべてのLast呼び出しが遅くなるか、配列のLastがO(N)を取得する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.