Aby odpowiedzieć na pytanie „dlaczego”, dlaczego nie List<T>
, powody są przyszłościowe i prostota API.
Gotowość na przyszłość
List<T>
nie jest zaprojektowany tak, aby można go było łatwo rozszerzać poprzez tworzenie podklas; został zaprojektowany tak, aby był szybki we wdrożeniach wewnętrznych. Zauważysz, że metody na nim nie są wirtualne i dlatego nie można ich zastąpić, i nie ma żadnych punktów zaczepienia w jego operacjach Add
/ Insert
/ Remove
.
Oznacza to, że jeśli będziesz musiał zmienić zachowanie kolekcji w przyszłości (np. Aby odrzucić obiekty puste, które ludzie próbują dodać, lub wykonać dodatkową pracę, gdy tak się stanie, np. Zaktualizować stan twojej klasy), musisz zmienić typ kolekcji, wrócisz do takiej, którą możesz podklasować, co będzie zepsutą zmianą interfejsu (oczywiście zmiana semantyki rzeczy, takich jak niedopuszczanie do wartości null, może być również zmianą interfejsu, ale rzeczy takie jak aktualizacja wewnętrznego stanu klasy nie byłyby).
Dlatego zwracając klasę, którą można łatwo podzielić na podklasę, taką jak Collection<T>
lub interfejs, taki jak IList<T>
, ICollection<T>
lub IEnumerable<T>
możesz zmienić wewnętrzną implementację, aby była innym typem kolekcji, aby spełnić Twoje potrzeby, bez łamania kodu konsumentów, ponieważ nadal można ją zwrócić jako rodzaj, jakiego oczekują.
Prostota interfejsu API
List<T>
zawiera wiele przydatnych funkcji, takich jak BinarySearch
, Sort
i tak dalej. Jednak jeśli jest to kolekcja, którą ujawniasz, prawdopodobnie kontrolujesz semantykę listy, a nie konsumentów. Więc chociaż twoja klasa wewnętrznie może potrzebować tych operacji, jest bardzo mało prawdopodobne, że konsumenci twojej klasy będą chcieli (lub nawet powinni) je wywoływać.
W związku z tym, oferując prostszą klasę kolekcji lub interfejs, zmniejszasz liczbę członków, których widzą użytkownicy Twojego interfejsu API, i ułatwiasz im korzystanie.
Osobiście zadeklarowałbym, że zwraca interfejs, a nie konkretną kolekcję. Jeśli naprawdę chcesz mieć dostęp do listy, użyj
IList<T>
. W przeciwnym razie rozważICollection<T>
iIEnumerable<T>
.źródło
We recommend using Collection<T>, ReadOnlyCollection<T>, or KeyedCollection<TKey,TItem> for outputs and properties and interfaces IEnumerable<T>, ICollection<T>, IList<T> for inputs.
CA1002 wydaje się zgadzać z komentarzami Krzysztofa. Nie mogę sobie wyobrazić, dlaczego konkretna kolekcja byłaby zalecana zamiast interfejsu i dlaczego rozróżnienie między danymi wejściowymi / wyjściowymi.ReadOnlyCollection<T>
nie miałoby to sensu w przypadku wkładu. Podobnie,IList<T>
jak mówi wejście, „Potrzebuję Sort () lub innego elementu członkowskiego, który ma IList”, co nie ma sensu dla danych wyjściowych. Chodziło mi jednak o to, dlaczego miałbymICollection<T>
być zalecany jako wkład iCollection<T>
jako wyjście. Dlaczego nie użyćICollection<T>
jako wyjścia również tak, jak sugerowałeś?Collection<T>
iReadOnlyCollection<T>
oba pochodzą zICollection<T>
(tj. nie maIReadOnlyCollection<T>
). Jeśli zwrócisz interfejs, nie jest oczywiste, który to jest i czy można go zmodyfikować. W każdym razie dziękuję za Twój wkład. To był dla mnie dobry test poczytalności.Chyba nikt jeszcze nie odpowiedział na część „dlaczego” ... więc proszę bardzo. Powodem, dla którego "powinieneś" używać a
Collection<T>
zamiast a,List<T>
jest to, że jeśli ujawnisz aList<T>
, to każdy, kto uzyska dostęp do twojego obiektu, będzie mógł modyfikować elementy na liście. NatomiastCollection<T>
ma wskazywać, że tworzysz własne metody „Dodaj”, „Usuń” itp.Prawdopodobnie nie musisz się tym martwić, ponieważ prawdopodobnie kodujesz interfejs tylko dla siebie (lub może kilku kolegów). Oto kolejny przykład, który może mieć sens.
Jeśli masz publiczną tablicę, np:
Można by pomyśleć, że ponieważ jest tylko akcesor „get”, którego nikt nie może zepsuć wartości, ale to nieprawda. Każdy może zmienić wartości w środku, tak jak to:
Osobiście po prostu używałbym
List<T>
w większości przypadków. Ale jeśli projektujesz bibliotekę klas, którą zamierzasz rozdać przypadkowym programistom i musisz polegać na stanie obiektów ... wtedy będziesz chciał stworzyć własną kolekcję i zablokować ją stamtąd: )źródło
Chodzi głównie o wyabstrahowanie własnych implementacji zamiast ujawniania obiektu List do bezpośredniej manipulacji.
Nie jest dobrą praktyką pozwalanie innym obiektom (lub ludziom) bezpośrednio modyfikować stan twoich obiektów. Pomyśl o metodach pobierających / ustawiających właściwości.
Kolekcja -> Dla zwykłej kolekcji
ReadOnlyCollection -> Dla kolekcji, które nie powinny być modyfikowane
KeyedCollection -> Gdy chcesz zamiast tego słowniki.
Sposób rozwiązania tego problemu zależy od tego, co ma robić Twoja klasa i od celu metody GetList (). Czy możesz rozwinąć?
źródło
ReadOnlyCollection
ile pozostałe dwa nie są mu posłuszne.GetList()
aby móc lepiej mu pomóc.Collection<T>
pomaga w abstrakcji wewnętrznej implementacji i zapobiega bezpośredniej manipulacji wewnętrzną listą. W jaki sposób?Collection<T>
jest tylko opakowaniem działającym na tej samej przekazanej instancji. To dynamiczna kolekcja przeznaczona do dziedziczenia, nic więcej (odpowiedź Grega jest tutaj bardziej odpowiednia).W tego rodzaju przypadkach zwykle staram się ujawnić jak najmniejszą ilość implementacji, jaka jest potrzebna. Jeśli konsumenci nie muszą wiedzieć, że faktycznie używasz listy, nie musisz jej zwracać. Wracając zgodnie z sugestią firmy Microsoft dotyczącą kolekcji, ukrywasz fakt, że używasz listy przed użytkownikami swojej klasy i izolujesz ich przed wewnętrzną zmianą.
źródło
Coś do dodania, chociaż minęło dużo czasu, odkąd o to zapytano.
Gdy typ listy pochodzi od
List<T>
zamiastCollection<T>
, nie można zaimplementować chronionych metod wirtualnych, któreCollection<T>
implementują. Oznacza to, że typ pochodny nie może odpowiedzieć w przypadku wprowadzenia jakichkolwiek modyfikacji na liście. Dzieje się tak, ponieważList<T>
zakłada się, że wiesz, kiedy dodajesz lub usuwasz elementy. Możliwość odpowiadania na powiadomienia to narzut i dlategoList<T>
go nie oferuje.W przypadkach, gdy kod zewnętrzny ma dostęp do Twojej kolekcji, możesz nie mieć kontroli nad dodawaniem lub usuwaniem elementu. Dlatego
Collection<T>
zapewnia sposób sprawdzenia, kiedy lista została zmodyfikowana.źródło
Nie widzę problemu ze zwrotem czegoś takiego
Jeśli zwróciłem odłączoną kopię danych wewnętrznych, albo odpisałem wynik zapytania o dane - mogę bezpiecznie wrócić
List<TItem>
bez ujawniania szczegółów implementacji i pozwolić na wygodne wykorzystanie zwróconych danych.Ale to zależy od tego, jakiego rodzaju konsumenta oczekuję - jeśli jest to coś w rodzaju siatki danych, wolę zwrócić,
IEnumerable<TItem>
która i tak będzie skopiowaną listą pozycji w większości przypadków :)źródło
Cóż, klasa Collection jest po prostu klasą opakowującą wokół innych kolekcji, aby ukryć szczegóły ich implementacji i inne funkcje. Myślę, że ma to coś wspólnego z właściwościami ukrywającymi wzorzec kodowania w językach zorientowanych obiektowo.
Myślę, że nie powinieneś się tym martwić, ale jeśli naprawdę chcesz zadowolić narzędzie do analizy kodu, wykonaj następujące czynności:
źródło
Collection<T>
O ile rozumiem, opakowanie w a nie chroni ani siebie, ani podstawowej kolekcji.Collection<T>
natychmiast ochroni twoją podstawową kolekcję.