W moim kodzie muszę używać IEnumerable<>
kilka razy, dlatego otrzymuję błąd Resharper „Możliwe wielokrotne wyliczenie IEnumerable
”.
Przykładowy kod:
public List<object> Foo(IEnumerable<object> objects)
{
if (objects == null || !objects.Any())
throw new ArgumentException();
var firstObject = objects.First();
var list = DoSomeThing(firstObject);
var secondList = DoSomeThingElse(objects);
list.AddRange(secondList);
return list;
}
- Mogę zmienić
objects
parametr na,List
a następnie uniknąć możliwego wielokrotnego wyliczenia, ale wtedy nie otrzymuję najwyższego obiektu, który mogę obsłużyć. - Inną rzeczą, którą mogę zrobić, to przekonwertować
IEnumerable
abyList
na początku metody:
public List<object> Foo(IEnumerable<object> objects)
{
var objectList = objects.ToList();
// ...
}
Ale to jest po prostu niezręczne .
Co byś zrobił w tym scenariuszu?
źródło
IReadOnlyCollection(T)
(nowy w .net 4.5) jako najlepszy interfejs do przekazania idei, że jest onIEnumerable(T)
przeznaczony do wielokrotnego liczenia. Jak stwierdza ta odpowiedź,IEnumerable(T)
sama w sobie jest tak ogólna, że może nawet odnosić się do treści, których nie można zresetować, których nie można ponownie wyliczyć bez skutków ubocznych. AleIReadOnlyCollection(T)
implikuje powtarzalność..ToList()
na początku metody, najpierw musisz sprawdzić, czy parametr nie ma wartości null. Oznacza to, że otrzymasz dwa miejsca, w których zgłaszasz ArgumentException: jeśli parametr ma wartość NULL i nie zawiera żadnych elementów.IReadOnlyCollection<T>
vs,IEnumerable<T>
a następnie opublikowałem pytanie dotyczące używaniaIReadOnlyCollection<T>
jako parametru oraz w jakich przypadkach. Myślę, że wniosek, do którego ostatecznie doszedłem, jest logiką do naśladowania. To powinno być stosowane tam, gdzie oczekuje się wyliczenie, nie koniecznie tam, gdzie może być wielokrotnością wyliczenie.Jeśli Twoje dane zawsze będą powtarzalne, być może nie przejmuj się nimi. Można go jednak również rozwinąć - jest to szczególnie przydatne, jeśli przychodzące dane mogą być duże (na przykład odczyt z dysku / sieci):
Uwaga: Zmieniłem nieco semantię DoSomethingElse, ale ma to głównie na celu pokazanie rozwiniętego użycia. Możesz na przykład ponownie owinąć iterator. Możesz zrobić z niego również blok iteratora, co może być miłe; wtedy nie ma
list
- i dostanieszyield return
przedmioty, jak je zdobędziesz, zamiast dodawać do listy, która ma zostać zwrócona.źródło
Użycie
IReadOnlyCollection<T>
lubIReadOnlyList<T>
w podpisie metody zamiastIEnumerable<T>
, ma tę zaletę, że wyraźnie oznacza, że może być konieczne sprawdzenie liczby przed iteracją lub wielokrotne iterowanie z innego powodu.Mają jednak ogromny minus, który spowoduje problemy, jeśli spróbujesz przeredagować swój kod, aby używać interfejsów, na przykład aby uczynić go bardziej testowalnym i przyjaznym dla dynamicznego proxy. Kluczową kwestią jest to, że
IList<T>
nie dziedziczyIReadOnlyList<T>
, i podobnie dla innych kolekcji i ich odpowiednich interfejsów tylko do odczytu. (Krótko mówiąc, to dlatego, że .NET 4.5 chciał zachować zgodność ABI z wcześniejszymi wersjami. Ale nawet nie skorzystali z okazji, aby zmienić to w .NET core. )Oznacza to, że jeśli dostaniesz
IList<T>
z jakiejś części programu i chcesz przekazać ją do innej części, która oczekujeIReadOnlyList<T>
, nie możesz! Możesz jednak przekazaćIList<T>
jakoIEnumerable<T>
.Ostatecznie
IEnumerable<T>
jest jedynym interfejsem tylko do odczytu obsługiwanym przez wszystkie kolekcje .NET, w tym wszystkie interfejsy kolekcji. Każda inna alternatywa wróci, by cię ugryźć, gdy zdasz sobie sprawę, że zablokowałeś się przed niektórymi opcjami architektury. Sądzę więc, że to właściwy typ w sygnaturach funkcji, aby wyrazić, że chcesz tylko kolekcję tylko do odczytu.(Pamiętaj, że zawsze możesz napisać
IReadOnlyList<T> ToReadOnly<T>(this IList<T> list)
metodę rozszerzenia, która po prostu rzutuje, jeśli typ bazowy obsługuje oba interfejsy, ale musisz dodać ją ręcznie wszędzie podczas refaktoryzacji, gdzie jakIEnumerable<T>
zawsze jest kompatybilna).Jak zawsze nie jest to absolutne, jeśli piszesz obszerny kod bazy danych, w którym przypadkowe wielokrotne wyliczenie byłoby katastrofą, możesz preferować inny kompromis.
źródło
Jeśli celem jest naprawdę zapobieganie wielokrotnym wyliczeniom, to odpowiedź należy do Marca Gravella, ale zachowując tę samą semantykę, możesz po prostu usunąć zbędne
Any
iFirst
wywołania i zastosować:Zauważ, że zakłada to, że nie jesteś
IEnumerable
ogólny lub przynajmniej jest ograniczony do typu odniesienia.źródło
objects
jest nadal przekazywany do DoSomethingElse, który zwraca listę, więc istnieje możliwość, że nadal wyliczasz dwa razy. Tak czy inaczej, jeśli kolekcja była zapytaniem LINQ to SQL, trafiłeś w DB dwukrotnie.First
spowoduje wygenerowanie wyjątku dla pustej listy. Nie sądzę, że można powiedzieć, że nigdy nie rzucaj wyjątków na pustą listę lub zawsze rzucaj wyjątki na pustą listę. To zależy ...W tej sytuacji zwykle przeciążam moją metodę IEnumerable i IList.
Staram się wyjaśnić w komentarzach podsumowujących metody, że wywołanie IEnumerable wykona funkcję .ToList ().
Programista może wybrać opcję .ToList () na wyższym poziomie, jeśli wiele operacji jest konkatenowanych, a następnie wywołać przeciążenie IList lub pozwolić, aby moje IEnumerable przeciążenie się tym zajęło.
źródło
Jeśli chcesz tylko sprawdzić pierwszy element, możesz zerknąć na niego bez iteracji całej kolekcji:
źródło
Po pierwsze, to ostrzeżenie nie zawsze tyle znaczy. Zwykle wyłączałem go po upewnieniu się, że nie jest to szyjka butelki wydajności. Oznacza to tylko, że
IEnumerable
ocena jest oceniana dwukrotnie, co zwykle nie stanowi problemu, chyba żeevaluation
samo zajmuje dużo czasu. Nawet jeśli zajmuje to dużo czasu, w tym przypadku używasz tylko jednego elementu za pierwszym razem.W tym scenariuszu można jeszcze bardziej wykorzystać potężne metody rozszerzenia linq.
W
IEnumerable
tym przypadku można ocenić tylko raz z pewnym kłopotem, ale najpierw profiluj i sprawdź, czy to naprawdę problem.źródło
IEnumerables
która daje różne wyniki za każdym razem, gdy ją powtarzasz, lub która zajmuje naprawdę dużo czasu. Zobacz odpowiedź Marca Gravellsa - Stwierdza on przede wszystkim: „może nie martw się”. Rzecz w tym, że wiele razy to widzisz, nie masz się czym martwić.