Kiedy piszę mój kod DAL lub inny kod, który zwraca zestaw elementów, czy powinienem zawsze składać instrukcję zwrotu:
public IEnumerable<FooBar> GetRecentItems()
lub
public IList<FooBar> GetRecentItems()
Obecnie w moim kodzie próbuję używać IEnumerable tak często, jak to możliwe, ale nie jestem pewien, czy jest to najlepsza praktyka? Wydawało się słuszne, ponieważ zwracałem najbardziej ogólny typ danych, jednocześnie opisując, co robi, ale być może nie jest to właściwe.
c#
ienumerable
KingNestor
źródło
źródło
Odpowiedzi:
To naprawdę zależy od tego, dlaczego używasz tego konkretnego interfejsu.
Na przykład
IList<T>
ma kilka metod, których nie ma wIEnumerable<T>
:IndexOf(T item)
Insert(int index, T item)
RemoveAt(int index)
i właściwości:
T this[int index] { get; set; }
Jeśli potrzebujesz tych metod w jakikolwiek sposób, na pewno wróć
IList<T>
.Ponadto, jeśli metoda, która zużywa twójIEnumerable<T>
wynik, oczekuje anIList<T>
, zapisze CLR przed rozważeniem wszelkich wymaganych konwersji, optymalizując w ten sposób skompilowany kod.źródło
Wytyczne dotyczące projektowania frameworka zalecają używanie Collection Collection, gdy trzeba zwrócić kolekcję, którą można modyfikować przez obiekt wywołujący lub ReadOnlyCollection dla kolekcji tylko do odczytu.
Powodem, dla którego jest to preferowane od prostego,
IList
jest to, żeIList
nie informuje dzwoniącego, czy jest tylko do odczytu, czy nie.Jeśli
IEnumerable<T>
zamiast tego zwrócisz an , niektóre operacje mogą być nieco trudniejsze do wykonania przez dzwoniącego. Ponadto nie będziesz już dawać dzwoniącemu elastyczności w modyfikowaniu kolekcji, czego możesz chcieć lub nie.Pamiętaj, że LINQ zawiera kilka sztuczek w rękawie i zoptymalizuje niektóre wywołania na podstawie typu, na którym są wykonywane. Na przykład, jeśli wykonasz a,
Count
a podstawową kolekcją jest List, NIE przejdzie ona przez wszystkie elementy.Osobiście w przypadku ORM prawdopodobnie pozostałbym
Collection<T>
jako wartość zwrotu.źródło
Ogólnie rzecz biorąc, powinieneś wymagać najbardziej ogólnej i zwrócić najbardziej szczegółową rzecz, jaką możesz. Więc jeśli masz metodę, która przyjmuje parametr i naprawdę potrzebujesz tylko tego, co jest dostępne w IEnumerable, to powinien być twój typ parametru. Jeśli Twoja metoda może zwrócić IList lub IEnumerable, preferuj zwracanie IList. Gwarantuje to, że jest on użyteczny przez najszerszą grupę konsumentów.
Nie przejmuj się tym, czego potrzebujesz, i jasno przedstaw to, co zapewniasz.
źródło
To zależy...
Zwracanie najmniejszego typu pochodnego (
IEnumerable
) pozostawi największą swobodę w zmianie podstawowej implementacji w dół ścieżki.Zwracanie bardziej pochodnego typu (
IList
) zapewnia użytkownikom interfejsu API więcej operacji na wyniku.Zawsze sugerowałbym zwrócenie najmniej pochodnego typu, który zawiera wszystkie operacje, których będą potrzebować Twoi użytkownicy ... więc w zasadzie najpierw musisz zastanowić się, jakie operacje na wyniku mają sens w kontekście definiowanego API.
źródło
Jedną rzeczą do rozważenia jest to, że jeśli używasz instrukcji LINQ z odroczonym wykonaniem, aby wygenerować swoją
IEnumerable<T>
, wywołanie.ToList()
przed powrotem z metody oznacza, że elementy mogą być iterowane dwukrotnie - raz w celu utworzenia listy i raz, gdy obiekt wywołujący przechodzi przez pętlę , filtruje lub przekształca zwracaną wartość. Jeśli jest to praktyczne, wolę unikać konwertowania wyników LINQ-to-Objects na konkretną listę lub słownik, dopóki nie będę musiał. Jeśli mój wywołujący potrzebuje listy, jest to jedna prosta metoda wywołania - nie muszę podejmować tej decyzji za niego, a to sprawia, że mój kod jest nieco bardziej wydajny w przypadkach, gdy wywołujący wykonuje tylko foreach.źródło
List<T>
oferuje kod wywołujący o wiele więcej funkcji, takich jak modyfikowanie zwracanego obiektu i dostęp przez indeks. Tak więc pytanie sprowadza się do tego: czy w konkretnym przypadku Twojej aplikacji CHCESZ wspierać takie zastosowania (prawdopodobnie zwracając świeżo skonstruowaną kolekcję!), Dla wygody dzwoniącego - czy też chcesz szybkości w prostym przypadku, gdy wszystkie potrzebą osoby dzwoniącej jest zapętlenie kolekcji i można bezpiecznie zwrócić odwołanie do prawdziwej kolekcji bazowej bez obawy, że spowoduje to błędną zmianę itp.?Tylko Ty możesz odpowiedzieć na to pytanie i tylko wtedy, gdy dobrze rozumiesz, co Twoi rozmówcy będą chcieli zrobić z wartością zwracaną i jak ważna jest tutaj wydajność (jak duże są kolekcje, które chcesz kopiować, jakie jest prawdopodobieństwo, że będzie to wąskie gardło, itp).
źródło
Jeśli kolekcja ma być przeznaczona tylko do odczytu lub modyfikacja kolekcji jest kontrolowana przez,
Parent
to zwrócenie „IList
just for”Count
nie jest dobrym pomysłem.W Linq istnieje
Count()
metoda rozszerzenia, doIEnumerable<T>
której wewnątrz środowiska CLR zostanie skrócony,.Count
jeśli typ bazowy toIList
, więc różnica wydajności jest pomijalna.Generalnie uważam (opinia), że lepszą praktyką jest zwrócenie IEnumerable tam, gdzie to możliwe, jeśli potrzebujesz dodać te metody, dodaj te metody do klasy nadrzędnej, w przeciwnym razie konsument zarządza kolekcją w ramach Modelu, co narusza zasady, np.
manufacturer.Models.Add(model)
Narusza prawo demeter. Oczywiście są to tylko wskazówki, a nie twarde i szybkie zasady, ale dopóki nie zrozumiesz w pełni możliwości ich zastosowania, podążanie na ślepo jest lepsze niż w ogóle.(Uwaga: jeśli używasz nNHibernate, może być konieczne zmapowanie do prywatnego IList przy użyciu różnych akcesorów).
źródło
Nie jest to takie proste, gdy mówisz o zwracanych wartościach zamiast o parametrach wejściowych. Kiedy jest to parametr wejściowy, wiesz dokładnie, co musisz zrobić. Tak więc, jeśli chcesz mieć możliwość iteracji kolekcji, weź IEnumberable, podczas gdy jeśli chcesz dodać lub usunąć, bierzesz IList.
W przypadku wartości zwracanej jest trudniej. Czego oczekuje Twój rozmówca? Jeśli zwrócisz IEnumerable, nie będzie on wiedział a priori, że może zrobić z tego IListę. Ale jeśli zwrócisz ILista, będzie wiedział, że może to powtórzyć. Musisz więc wziąć pod uwagę, co twój rozmówca zrobi z danymi. Funkcjonalność, której Twój rozmówca potrzebuje / oczekuje, jest tym, co powinno rządzić przy podejmowaniu decyzji o tym, co zwrócić.
źródło
jak wszyscy powiedzieli, to zależy, jeśli nie chcesz dodawać / usuwać funkcjonalności przy wywoływaniu warstwy, zagłosuję na IEnumerable, ponieważ zapewnia on tylko iterację i podstawową funkcjonalność, która w perspektywie projektowej mi się podoba. Wracając do IList, moje głosy zawsze się powtarzają, ale chodzi głównie o to, co lubisz, a co nie. pod względem wydajności myślę, że są bardziej takie same.
źródło
Jeśli nie liczysz w kodzie zewnętrznym, zawsze lepiej jest zwrócić IEnumerable, ponieważ później możesz zmienić swoją implementację (bez wpływu na kod zewnętrzny), na przykład dla iteratora yield logiki i oszczędzania zasobów pamięci (przy okazji bardzo dobra funkcja języka ).
Jeśli jednak potrzebujesz liczby elementów, nie zapominaj, że między IEnumerable a IList istnieje inna warstwa - ICollection .
źródło
Mogę być tutaj trochę zdziwiony, widząc, że nikt inny tego nie zasugerował, ale dlaczego nie zwrócisz
(I)Collection<T>
?Z tego, co pamiętam,
Collection<T>
był preferowanym typem zwracanym,List<T>
ponieważ odciąga implementację. Wszystkie realizująIEnumerable
, ale wydaje mi się to trochę za niskie do tej pracy.źródło
Myślę, że możesz użyć jednego z nich, ale każdy ma zastosowanie. Zasadniczo
List
jest,IEnumerable
ale masz funkcjonalność liczenia, dodaj element, usuń elementIEnumerable
nie jest wydajne do liczenia elementów lub pobierania określonego elementu w kolekcji.List
to zbiór, który idealnie nadaje się do wyszukiwania konkretnych elementów, łatwego dodawania elementów lub ich usuwania.Generalnie staram się używać
List
tam, gdzie to możliwe, ponieważ daje mi to większą elastyczność.Użyj
List<FooBar> getRecentItems()
zamiastIList<FooBar> GetRecentItems()
źródło
Myślę, że ogólną zasadą jest użycie bardziej szczegółowej klasy do powrotu, aby uniknąć wykonywania niepotrzebnej pracy i dać dzwoniącemu więcej opcji.
To powiedziawszy, myślę, że ważniejsze jest rozważenie kodu przed sobą, który piszesz, niż kodu, który napisze następny facet (w granicach rozsądku). Dzieje się tak, ponieważ możesz przyjąć założenia dotyczące kodu, który już istnieje.
Pamiętaj, że przejście w górę do kolekcji z IEnumerable w interfejsie będzie działać, przejście w dół do IEnumerable z kolekcji spowoduje uszkodzenie istniejącego kodu.
Jeśli wszystkie te opinie wydają się sprzeczne, to dlatego, że decyzja jest subiektywna.
źródło
TL; DR; - Podsumowanie
List
) dla wartości zwracanych i najbardziej ogólnego typu dla parametrów wejściowych, nawet w przypadku kolekcji.IReadOnlyList
lubIReadOnlyCollection
jako typu wartości zwracanej.Więcej
źródło