Чому я повинен використовувати Список <T> над IEnumerable <T>?


24

У своєму веб-додатку ASP.net MVC4 я використовую IEnumerables, намагаючись слідувати за мантрою, щоб програмувати інтерфейс, а не реалізацію.

Return IEnumerable(Of Student)

проти

Return New List(Of Student)

Люди говорять мені використовувати List, а не IEnumerable, тому що списки змушують виконувати запит, а IEumerable цього не робить.

Це справді найкраща практика? Чи є альтернатива? Я дивно використовую конкретні об'єкти, де міг би використовуватися інтерфейс. Чи виправдане моє дивне почуття?


2
Е, по-перше, чому добре змусити виконати запит? По-друге, вам слід проводити моніторинг та профілювання дзвінків до бази даних, щоб оцінити, чи має ця технічна розвага будь-яка заслуга.
user16764

2
Кажуть, що всі запити повинні бути виконані так, щоб модель була виконана і завантажена готовою для перегляду. Тобто перегляд повинен отримувати все, а не запитувати базу даних.
Роуан Фрімен

3
Це питання StackOverflow досить добре висвітлює його.
Карл Білефельдт

Це гарна відповідь, але я хочу знати, що це стосується MVC. Чому перегляду не можна надати IEnumerables, щоб усі запити виконувалися вчасно?
Rowan Freeman

2
"Кажуть, що всі запити повинні бути виконані так, що модель виконана і завантажена готовою до перегляду. Тобто представлення має отримувати все, а не запитувати базу даних." Це нісенітниця. Якщо ви передаєте IEnumerable, ваш погляд не знає і не цікавить, чи це запит до бази даних. І саме так і має бути.
user16764

Відповіді:


21

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

Коротше кажучи, використовуйте в IEnumerableбудь-який час, IListколи вам потрібна лише ітерація, використовуйте, коли вам потрібно індексувати безпосередньо та потрібен масив динамічного розміру (якщо вам потрібна індексація на масиві фіксованого розміру, тоді просто використовуйте стандартний масив).

Що стосується часу виконання, ви завжди можете використовувати список як IEnumerableзмінну, тому сміливо повертайте команду IEnumerablea .ToList();, або передаючи параметр у вигляді IEnumerableвиконання, виконуючи .ToList()на виконання IEnumerableта примусового виконання виконання. Будьте обережні, що в будь-який момент, коли ви змушуєте виконувати з .ToList()вами виконання, ви не будете зациклюватися на IEnumerableзмінній, яку ви тільки що зробили для цього, і виконати її знову, інакше ви в кінцевому підсумку подвійно повторите ітерації в запиті LINQ.

Що стосується MVC, то тут немає нічого особливого для зауваження. Це дотримуватиметься тих самих правил часу виконання, що і решта .NET, я думаю, у вас може виникнути хтось, хто був розгублений, викликаний семантикою затримки виконання в минулому, і звинувачував це в MVC, кажучи вам, що це якось пов'язано, але це ні. Семантика затримки виконання спочатку всіх бентежить (і навіть надовго; потім вони можуть бути хитрими). Знову ж таки, просто не хвилюйтеся з цього приводу, поки ви дійсно не потурбуєтеся про те, щоб запит LINQ не виконувався двічі або не вимагав його виконувати в певному порядку відносно іншого коду, після чого призначте свою змінну собі.ToList () щоб примусити стратити, і ти будеш добре.


Чи погано давати перегляд IEnumerables? Якщо ви даєте йому списки, щоб запити виконувались до моменту їх перегляду?
Rowan Freeman

@RowanFreeman Я просто додав редагування, щоб відповісти на це. Я думаю, що у вас є хтось, хто наткнувся на те, що вони не зовсім зрозуміли (не можу їх звинуватити, затримка виконання серйозно складна і заплутана) і викликала це до поганого Joojoo замість того, щоб працювати, щоб зрозуміти повну поведінку затримки виконання семантика.
Джиммі Хоффа

Гарна відповідь. Тож мені колись справді потрібно буде використовувати .ToList ()? Поки мій додаток працює чудово, використовуючи лише IEnumerables і передаючи їх з моделі на перегляд. Немає списку або .ToList (). Моє питання не є функціональним - я знаю, що моя програма працює. Моє питання - одна з найкращих практик.
Роуан Фрімен

4
Найкраща практика @RowanFreeman - використовувати мінімальний інтерфейс, який все ще відповідає вашим вимогам. IEnumerable робить це зараз саме для вас, тому не хвилюйтеся про її зміну. Однак, настане день, коли у вас буде запит, який виконується 3 або 10 разів, і ви не розумієте, чому, або очікуєте, що запит буде виконаний перед вставкою, щоб знайти його після цього, саме ці часи потрібно визнати. () примушує виконання, коли ви хочете, і повторення IEnumerable з LINQ-запиту кілька разів виконає весь запит кілька разів; Виправте страту, коли трапляються ці події
Джиммі Хоффа

1
Ви навіть не повинні використовувати - Listпропуск навколо Listозначає, що зміст списку буде змінено. Якщо ви хочете повернути колекцію, використовуйте IReadOnlyCollection. Listпризначений для використання в межах методів та для обміну між методами, що змінюють список. Це воно!
ЕрікЕ

7

Є два питання.

IENumerable<Data> query = MyQuery();

//Later
foreach (Data item in query) {
  //Process data
}

До досягнення циклу "Обробляти дані" запит може більше не бути дійсним. Наприклад, якщо запит виконується на вже розміщеному DataContext, ваш код видасть виняток. Така річ стає дуже заплутаною, коли ви обробляєте запит в іншому контексті, ніж там, де ви його створили.

Друга проблема полягає в тому, що ваше з'єднання не буде випущено, поки цикл "Обробляти дані" не завершиться. Це лише проблема, якщо "Обробляти дані" є складним. Про це йдеться на веб- сторінці http://msdn.microsoft.com/en-us/library/bb386929.aspx :

Q. Як довго моє підключення до бази даних залишається відкритим?

A. З'єднання, як правило, залишається відкритим, поки ви не споживаєте результати запиту. Якщо ви очікуєте, що знадобиться час для обробки всіх результатів, і ви не проти кешування результатів, застосуйте ToList до запиту. У загальних сценаріях, коли кожен об'єкт обробляється лише один раз, потокова модель перевершує і DataReader, і LINQ, ніж SQL.

Отже, ці питання викликають те, що вас заохочують переконатися, що запит справді виконується, наприклад, дзвонивши ToList(). Однак, як пропонує Джиммі , ніщо не заважає повернути Ваш список як безліч.

Як правило, я рекомендую уникати повторень над IEumerable більше ніж один раз. Припускаючи, що споживачі вашого коду дотримуються цього правила, я не вважаю занепокоєння тим, що хтось може двічі потрапити в базу даних, виконавши запит двічі.


1

Ще однією перевагою перерахунку IEnumerableраннього є винятки, які будуть викинуті у відповідному місці. Це допомагає налагоджувати.

Наприклад, якщо ви отримали виняток із тупикової ситуації в одному з представлень Razor, це було б не так зрозуміло, як якщо б виняток стався під час одного з ваших методів доступу до даних.


Будь-який метод, що повертає IEnumerableте, що може кинути, ймовірно, робить помилку. Відкладені IEnumerableметоди слід розділити на два: один не відкладений метод перевіряє параметри та налаштовує, при необхідності викидаючи (скажімо, через нульовий аргумент). Потім він повертає виклик до приватної реалізації , яка є відкладеним. Я не думаю, що мої коментарі повністю не суперечать вашій відповіді, але думаю, що ви залишили важливий аспект у своїй відповіді, який полягає в семантичному значенні використання IEnumerableпроти List(мутації) проти IReadOnlyCollection(ніякої користі для відстрочка) .
ЕрікЕ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.