2013-11-21 12 views
58

Czasami Resharper ostrzega o:ReSharper dla wyjaśniając "Możliwe wielokrotne wyliczanie IEnumerable"

Możliwość wielokrotnego wyliczenie IEnumerable

Jest an SO question on how to handle this issue, a strona ReSharper wyjaśnia również rzeczy here. To ma jakiś przykładowy kod, który mówi ci to zrobić w zamian:

IEnumerable<string> names = GetNames().ToList(); 

Moje pytanie dotyczy tego konkretnego sugestia: nie będzie to nadal prowadzić poprzez wyliczanie kolekcji dwa razy w ciągu 2 for-each pętli?

Odpowiedz

146

GetNames() zwraca wartość IEnumerable. Więc jeśli przechowywać ten wynik:

IEnumerable foo = GetNames(); 

Potem za każdym razem wyliczyć foo, metoda GetNames() nazywa się ponownie (nie dosłownie, nie mogę znaleźć link, który właściwie wyjaśnia szczegóły, ale zobaczyć IEnumerable.GetEnumerator()).

Resharper widzi to, i sugeruje, aby zapisać wynik z wyliczanie GetNames() w zmiennej lokalnej, na przykład przez materializuje je w formie listy:

IEnumerable fooEnumerated = GetNames().ToList(); 

To będzie upewnić się, że wynik GetNames() jest wyliczane tylko jeden raz, pod warunkiem, że odnosisz się do fooEnumerated.

Ma to znaczenie, ponieważ zwykle chcesz wyliczyć tylko raz, na przykład, gdy GetNames() wykonuje (powolne) wywołanie bazy danych.

Ponieważ zmaterializował wyników na liście, nie ma już znaczenia, że ​​wyliczysz fooEnumerated dwa razy; będziesz dwukrotnie przeglądał listę w pamięci.

+0

ah! To wyjaśnia to. – user2250250

+0

Nie. Metoda GetEnumerator() zostanie wywołana tylko raz w pętli foreach. Prawdziwy powód to ryzyko zabrudzenia danych. Na przykład w GetNames() istnieje zapytanie SQL, ale tylko zapytanie zwracające IEnurable. Kiedy wywołasz .ToList(), przechowujesz wszystkie dane w pamięci, istnieje niewielkie ryzyko brudnych danych.Ale jeśli pomiędzy 2 opcjami pętli jest dużo czasu, jeśli za każdym razem wykluczysz SQL do bazy danych, istnieje duże ryzyko zabrudzenia danych. –

+0

@SunRobin jest to odpowiedź, która przedstawia prawdę w uproszczonej formie, jak również jest w niej wspomniana. Nie udało mi się jeszcze tego poprawić. Korzystanie z "brudnych danych" może wymagać dodatkowych wyjaśnień. – CodeCaster

4

Tak, będziesz wymieniać go dwukrotnie bez wątpienia. ale chodzi o to, że jeśli GetNames() zwróci leniwą kwerendę linq, która jest bardzo kosztowna do obliczenia, wówczas będzie obliczać dwukrotnie bez wywoływania ToList() lub ToArray().

7

GetNames() nie jest wywoływana dwa razy. Implementacja IEnumerable.GetEnumerator() jest wywoływana za każdym razem, gdy chcesz wyliczyć kolekcję za pomocą foreach. Jeśli w ramach IEnumerable.GetEnumerator() zostaną wykonane pewne kosztowne obliczenia, może to być powód do rozważenia.