Zobacz definicję klasy System.Array
public abstract class Array : IList, ...
Teoretycznie powinienem być w stanie napisać ten fragment i być szczęśliwy
int[] list = new int[] {};
IList iList = (IList)list;
Powinienem również móc wywołać dowolną metodę z iList
ilist.Add(1); //exception here
Moje pytanie nie brzmi: dlaczego otrzymuję wyjątek, ale raczej dlaczego Array implementuje IList ?
c#
arrays
ilist
liskov-substitution-principle
oleksii
źródło
źródło
Odpowiedzi:
Ponieważ tablica umożliwia szybki dostęp przez indeks, a
IList
/IList<T>
to jedyne interfejsy kolekcji, które to obsługują. Więc może Twoje prawdziwe pytanie brzmi: „Dlaczego nie ma interfejsu dla stałych kolekcji z indeksatorami?” I na to nie mam odpowiedzi.Nie ma też interfejsów tylko do odczytu dla kolekcji. I brakuje mi tych nawet bardziej niż stałego rozmiaru z interfejsem indeksatorów.
IMO powinno być kilka dodatkowych (ogólnych) interfejsów do zbierania danych, w zależności od cech zbioru. Nazwy też powinny być inne,
List
bo coś z indeksatorem to naprawdę głupie IMO.IEnumerable<T>
ICollection<T>
IList<T>
Myślę, że obecne interfejsy kolekcji są źle zaprojektowane. Ale ponieważ mają właściwości, które mówią ci, które metody są prawidłowe (i jest to część kontraktu tych metod), nie łamie zasady substytucji.
źródło
add
i dlatego nie może być zastąpiona czymś, co robi, gdy ta umiejętność jest wymagana.IsFixedSize
iIsReadOnly
, zdecydowanie narusza zasadę Tell, Don't Ask i Zasadę najmniejszego zaskoczenia . Po co implementować interfejs, skoro zamierzasz tylko zgłosić wyjątki dla 4 z 9 metod?Uwag sekcję dokumentacji dla
IList
mówiOczywiście tablice należą do kategorii o stałym rozmiarze, więc ze względu na definicję interfejsu ma to sens.
źródło
Array
tegoAdd
wyraźnie implementuje metodę, co zmniejsza ryzyko przypadkowego wywołania jej.IList
która zabrania zarówno modyfikacji, jak i dodawania / usuwania. Wtedy dokumentacja nie jest już poprawna. : PPonieważ nie wszystkie
IList
s są zmienne (zobaczIList.IsFixedSize
iIList.IsReadOnly
), a tablice z pewnością zachowują się jak listy o stałym rozmiarze.Jeśli twoje pytanie naprawdę brzmi „dlaczego implementuje nieogólny interfejs”, to odpowiedź jest taka, że były one w pobliżu, zanim pojawiły się generyczne.
źródło
IList
sam interfejs mówi ci, że może nie być zmienny. Gdyby faktycznie gwarantowano, że będzie zmienny, a tablica mówi inaczej, złamałoby to regułę.IList<T>
i nie psuje go w przypadku nieogólnegoIList
: enterprisecraftsmanship.com/2014/11/22/…To dziedzictwo, które mamy z czasów, gdy nie było jasne, jak radzić sobie z kolekcjami tylko do odczytu i czy Array jest tylko do odczytu. W interfejsie IList istnieją flagi IsFixedSize i IsReadOnly. Flaga IsReadOnly oznacza, że nie można w ogóle zmienić kolekcji, a IsFixedSize oznacza, że kolekcja umożliwia modyfikację, ale nie pozwala na dodawanie ani usuwanie elementów.
W czasie .Net 4.5 było jasne, że niektóre „pośrednie” interfejsy są wymagane do pracy przy zbiorach tylko do odczytu, więc
IReadOnlyCollection<T>
iIReadOnlyList<T>
zostały wprowadzone.Oto świetny wpis na blogu opisujący szczegóły: Kolekcje tylko do odczytu w .NET
źródło
Definicja interfejsu IList to „Reprezentuje nieogólną kolekcję obiektów, do których można uzyskać dostęp za pomocą indeksu”. Tablica całkowicie spełnia tę definicję, więc musi implementować interfejs. Wyjątkiem podczas wywoływania metody Add () jest „System.NotSupportedException: kolekcja miała stały rozmiar” i wystąpił, ponieważ tablica nie może dynamicznie zwiększać swojej pojemności. Jego pojemność jest określana podczas tworzenia obiektu tablicy.
źródło
Posiadanie tablicy implementującej IList (i przejściowo ICollection) uprościło aparat Linq2Objects, ponieważ rzutowanie IEnumerable na IList / ICollection działałoby również dla tablic.
Na przykład Count () kończy wywołanie Array.Length pod maską, ponieważ jest rzutowana na ICollection, a implementacja tablicy zwraca Length.
Bez tego silnik Linq2Objects nie byłby specjalnie traktowany dla tablic i działałby strasznie, albo musieliby podwoić kod, dodając specjalne traktowanie tablic (tak jak w przypadku IList). Musieli zamiast tego zdecydować się na implementację IList w tablicach.
To moje podejście do „Dlaczego”.
źródło
Również szczegóły implementacji LINQ Last sprawdza dla IList, jeśli nie zaimplementował listy, potrzebowałby albo 2 kontroli spowalniających wszystkie ostatnie wywołania, albo mieć Last on an Array biorąc O (N)
źródło