Приклад Resharper-коду для пояснення "Можливе багаторазове перерахування IEnumerable"


76

Іноді Решарпер попереджає про:

Можливе багаторазове перерахування IEnumerable

Там в SO питання про те , як впоратися з цією проблемою , і сайт ReSharper також пояснює речі тут . У ньому є зразок коду, який підказує вам зробити це замість цього:

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

Моє запитання стосується цієї конкретної пропозиції: чи все одно це не призведе до перерахування колекції двічі у 2 цикли for-for?


Відповіді:


184

GetNames()повертає IEnumerable. Отже, якщо ви зберігаєте цей результат:

IEnumerable foo = GetNames();

Потім кожного разу, коли ви перелічуєте foo, GetNames()метод викликається знову (не буквально, я не можу знайти посилання, яке правильно пояснює деталі, але бачу IEnumerable.GetEnumerator()).

Resharper це бачить і пропонує вам зберегти результат перерахування GetNames()в локальній змінній, наприклад, матеріалізуючи його у списку:

IEnumerable fooEnumerated = GetNames().ToList();

Це забезпечить GetNames()перерахування результату лише один раз, якщо ви на це посилаєтесь fooEnumerated.

Це має значення, оскільки зазвичай ви хочете нумерувати лише один раз, наприклад, коли GetNames()виконується (повільний) виклик бази даних.

Оскільки ви матеріалізували результати у списку, більше не має значення, що ви перераховуєте fooEnumeratedдвічі; ви будете двічі перебирати список в пам'яті.


ах! Це пояснює це.
user2250250

1
Ні. Метод GetEnumerator () буде викликаний лише один раз у циклі foreach. Справжня причина полягає в ризику брудних даних. Наприклад, у GetNames () є запит SQL, але лише запит, який повертає IEnurable. Коли ви викликаєте .ToList (), ви зберігаєте всі дані в пам'яті, існує невеликий ризик брудних даних. Але якщо між двома циклами є багато часу, якщо ви щоразу вилучаєте SQL до бази даних, то існує великий ризик брудних даних.
Sun Robin

6
Це URL-адреса від Microsoft, щоб повідомити нам про внутрішню реалізацію циклу foreach. Ви бачите, що GetEnumerator () викликається лише один раз. І ще одне, що нам потрібно знати, це те, що IEnumerable - це ліниве завантаження, коли воно працює з якоюсь ORM. msdn.microsoft.com/en-us/library/aa664754(v=vs.71).aspx Якщо ви просто отримуєте обробник Enumerator і не завантажуєте всі дані в пам'ять, у вас можуть виникнути конфлікти між двома операціями з той же перерахувач, хтось змінив базу даних. Щоб уникнути цього, Resharper пропонує вам завантажити дані в Merroy за допомогою методу .ToList ().
Sun Robin

6
Ця відповідь є дещо неточною. Не кожен IEnumerableоцінюється для кожного перерахування, лише ті, хто реалізований із `` відкладеним виконанням '' (це посилання, якого вам не вистачало, @CodeCaster). Попередження R # можна безпечно ігнорувати, коли основна реалізація не відкладена, як у цьому прикладі запитання, який вимагає ToList()зберігати його в іншій IEnumerableзмінній.
Фредерік

2
@CodeCaster, не біда, я виходив із недавньої відповіді, що посилається туди, і додав до неї коротке пояснення того, чого бракувало imo. Не соромтеся повторно використовувати його, якщо хочете. Тоді я, мабуть, видалю свої коментарі тут, коли їхні помилки більше не будуть актуальними.
Фредерік

11

Я виявив, що це найкращий і найпростіший спосіб зрозуміти численні переліки.

C # LINQ: Можливе багаторазове перерахування IEnumerable

https://helloacm.com/c-linq-possible-multiple-enumeration-of-ienumerable-resharper/


Ви можете вказати особливі моменти вашого посилання тут .. але посилання приємне -> +1
LuckyLikey

Це чудове та легке пояснення на прикладі!
декомпільовано

9

GetNames()не викликається двічі. Реалізація IEnumerable.GetEnumerator()викликається кожного разу, коли ви хочете перерахувати колекцію за допомогою foreach. Якщо IEnumerable.GetEnumerator()зроблено якийсь дорогий розрахунок, це може бути приводом для розгляду.


5

Так, ти будеш перераховувати це двічі без сумнівів. але справа в тому, що якщо GetNames()повертає лінивий запит linq, який дуже дорогий для обчислення, тоді він буде обчислюватися двічі без виклику ToList()або ToArray().

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.