Różnica między różnymi interfejsami ogólnymi kolekcji w języku C #

11

Od jakiegoś czasu gram z C # dla Windows i ASP.net MVC. Ale nadal jestem niejasny w kilku obszarach. Próbuję zrozumieć podstawową różnicę między problemami z wydajnością a używaniem i zamianą podobnych rodzajów Ogólnych interfejsów kolekcji .

Jaka jest podstawowa różnica między IEnumerable<T>, ICollection<T>, List<T>(Class)?

Wydaje mi się, że używam ich i wymieniam, nie widząc żadnych problemów w moich aplikacjach. Czy istnieją jeszcze podobne ogólne zbiory, które można wymienić na te trzy?

Pankaj Upadhyay
źródło
2
jest też IList <T>
jk.

Odpowiedzi:

19

Lista <T> jest klasą i implementuje zarówno interfejs ICollection <T>, jak i IEnumerable <T> . Ponadto ICollection <T> rozszerza interfejs IEnumerable <T>. Nie są one wymienne, przynajmniej nie ze wszystkich punktów widzenia.

Jeśli masz List <T>, masz gwarancję, że ten obiekt implementuje metody i właściwości wymagane do zaimplementowania przez interfejs ICollection <T> i IEnumerable <T>. Kompilator wie o tym i możesz niejawnie rzutować je „na ICollection <T> lub IEnumerable <T>. Jeśli jednak masz ICollection <T>, musisz najpierw wyraźnie sprawdzić w kodzie, czy jest to List <T>, czy coś innego, być może Słownik <T> (gdzie T to KeyValuePair ) przed rzutowaniem go na to, co pragnienie.

Wiesz, że ICollection rozszerza IEnumerable, więc możesz przerzucić go na IEnumerable. Ale jeśli masz tylko IEnumerable, ponownie nie masz gwarancji, że jest to lista. Być może, ale może być coś innego. Powinieneś spodziewać się niepoprawnego wyjątku rzutowania, jeśli spróbujesz na przykład rzucić Listę <T> na Słownik <T>.

Nie są więc „wymienne”.

Ponadto istnieje wiele ogólnych interfejsów, sprawdź, co można znaleźć w przestrzeni nazw System.Collections.Generic .

Edycja: jeśli chodzi o twój komentarz, absolutnie nie ma ograniczenia wydajności, jeśli używasz List <T> lub jednego z interfejsów, które implementuje. Nadal będziesz musiał utworzyć nowy obiekt, sprawdź następujący kod:

List<T> list = new List<T>();
ICollection<T> myColl = list;
IEnumerable<T> myEnum = list;

List , myColl i myEnum wszystkie wskazują na ten sam obiekt. Niezależnie od tego, czy zadeklarujesz go jako List, ICollection, czy IEnumerable, nadal wymagam od programu utworzenia Listy. Mógłbym napisać to:

ICollection<T> myColl = new List<T>();

myColl , w czasie wykonywania jest nadal listą.

Jednakże , to najważniejszy punkt ... aby ograniczyć powstawanie i wzrost konserwacji należy zawsze deklarować zmienne i parametry Metoda z zastosowaniem najniższą możliwą mianownik możliwe dla ciebie, czy jest to interfejs lub abstrakcyjne lub betonu klasy.

Wyobraź sobie, że jedyną rzeczą, której potrzebuje metoda „PerformOperation”, jest wyliczenie elementów, wykonanie pracy i wyjście, w takim przypadku nie potrzebujesz stu dodatkowych metod dostępnych na liście <T>, potrzebujesz tylko tego, co jest dostępne w IEnumerable <T >, więc powinny mieć zastosowanie następujące zasady:

public void PerformOperation(IEnumerable<T> myEnumeration) { ... }

W ten sposób ty i inni programiści wiecie, że dowolny obiekt klasy implementujący interfejs IEnumerable <T> może zostać przekazany tej metodzie. Może to być lista, słownik lub niestandardowa klasa kolekcji, którą napisał inny programista.

Jeśli wręcz przeciwnie, wyraźnie zaznaczysz, że potrzebujesz konkretnej Listy <T> (i chociaż w rzeczywistości tak rzadko się zdarza, może się zdarzyć), Ty i inni programiści wiecie, że musi to być Lista lub inna dziedzicząca klasa z listy.

Jalayn
źródło
Przede wszystkim dzięki. Teraz chodzi o to, jak zdecydować, którego użyć? Ponieważ List <T> implementuje oba interfejsy, dlaczego nie zawsze używać go w każdym miejscu, w którym wymagany jest IEnumerable lub ICollection. Czy zawsze używanie listy będzie miało wpływ na wydajność? Edytuj swoją odpowiedź, aby odpowiedzieć na te obawy
Pankaj Upadhyay
Zredagowałem, aby odpowiedzieć na pytania dotyczące wydajności
Jalayn,
+1, ale dodam, że w rzadkich przypadkach, gdy tak naprawdę potrzebujesz przekazać listę do metody, powinieneś użyć IListraczej niż Listw deklaracji metody.
Konamiman,
@Konamiman - Zależy, czy potrzebujesz List<>określonych funkcji, takich jak .AddRange(). IList<>Nie narażać tego.
Bobson
6

Zajrzyj na strony MSDN dla ICollection i IEnumerable .

Mówiąc bardzo abstrakcyjnie, tak wyobrażam sobie te typy.

IEnumerable to wszystko, co można wyliczyć - to znaczy iterować. Nie musi to oznaczać „kolekcji”; na przykład IQueryable implementuje IEnumerable i nie jest kolekcją, ale jest czymś, co można zapytać o zwracane obiekty. Aby zaimplementować IEnumerable, obiekt musi mieć możliwość zwrócenia obiektu po zapytaniu. Mógłbym powiedzieć, że mam Wyliczenie zadań, które zamierzam dzisiaj wykonać (nie jest to lista, ponieważ nie spisałem jej ani nie sformułowałem, ale mogę powiedzieć, co mam teraz zrobić, i jeśli zapytałeś „a potem?”, mógłbym powiedzieć następujące zadanie).

ICollection jest bardziej konkretny niż IEnumerable. Kluczową różnicą jest to, że Kolekcja wie, ile zawiera przedmiotów; aby obliczyć, ile przedmiotów znajduje się w Wyliczaczu, efektywnie przeglądasz i policz je:

Ja: Chłopaki, ile przedmiotów macie w sobie?

Wymienny: Naprawdę nie wiem. Oto jeden przedmiot. Mam inny, więc to dwa. I jeszcze jeden, więc to trzy ... i jeszcze jeden ... ok, więc to 2382. Żadnych więcej przedmiotów, więc mam 2382. Nie pytaj mnie ponownie, bo będę musiał je wszystkie przejrzeć jeszcze raz.

Kolekcja: Mam 2382 przedmiotów. Już to wiem.

Kolekcja jest zwykle czymś, co już wie, gdzie znajdują się wszystkie jej elementy i nie trzeba jej szukać ani generować, gdy o nią poprosisz. Jest bardziej ... konkretny niż Enumerable.

Moim zdaniem różnica między Enumerable a Collection jest znacznie większa niż różnica między Collection a List. W rzeczywistości staram się wymyślić praktyczną różnicę między kolekcją a listą, ale uważam, że lista zapewnia lepsze metody wyszukiwania i porządkowania.

Inne rodzaje kolekcji to Queuei Stack, jak również Dictionary.

Nazwy tego typu są bardzo przydatne - możesz wyobrazić sobie kolejkę i stos jako ich odpowiedniki w świecie rzeczywistym i rozważyć różnice, jakie możesz mieć względem Listy.

Kirk Broadhurst
źródło
„Trudno mi wymyślić praktyczną różnicę między kolekcją a listą” ... Później w swojej odpowiedzi sam na to odpowiesz. ListJest ICollection, ale ICollectionnie zawsze jest List( Queue, Stack, Dictionary, ...)
Steven Jeuris
@Steven To bardzo chaotyczna odpowiedź. Zgadzam się, że to ważna różnica, ale nie nazwałbym tego praktyczną różnicą. Co to oznacza dla użytkownika?
Kirk Broadhurst
To łatwe! :) Queue<int> queue = (Queue<int>)list;rzuci InvalidCastExceptionkiedy listnie jest Queue<int>. Różnica między kolejką a listą jest poza zakresem tego pytania.
Steven Jeuris,
Nie jest krytycznym aspektem IEnumerable, że może zwrócić bieżące i następne elementy - przypominasz coś do tego, ale nie mów wprost. Zasadniczo coś, co implementuje sam IEnumerable, nie może zwrócić dowolnego elementu swoich członków, gdy jest on pytany - tylko bieżący i następny.
cori
@cori To bardzo zwięzły sposób, dużo jaśniejszy niż moja odpowiedź.
Kirk Broadhurst
-1

IQueryable:

kwerenda nie jest wykonywana, dopóki nie zostanie wykonana iteracja elementów, być może przez wykonanie funkcji .ToList ()

IEnumerable:

lista elementów tylko do przodu. Nie można dostać się do „przedmiotu 4” bez podania przedmiotów 0-3. lista tylko do odczytu, nie można do niej dodawać ani usuwać. Nadal może korzystać z odroczonego wykonania.

IList:

losowy dostęp do pełnej listy całkowicie w pamięci obsługuje dodawanie i usuwanie

ICollection:

Jest między IEnumerable i IList. To, co jest „najlepsze”, zależy od twoich wymagań. Zazwyczaj jednak IEnumerable jest „wystarczająco dobry”, jeśli chcesz tylko wyświetlać elementy. Przynajmniej zawsze używaj wariantu ogólnego.

Zia Qammar
źródło
ta odpowiedź nie wydaje się dodawać nic cennego w stosunku do tego, co zostało już opublikowane we wcześniejszych odpowiedziach
gnat,
to po prostu prosta koncepcja o wszystkim
Zia Qammar,