Багато було сказано раніше, але повернемось до коріння більш технічним способом:
IEnumerable це сукупність об'єктів в пам'яті, які ви можете перерахувати - послідовність в пам'яті, яка дозволяє повторити процес (полегшує проходження в foreachциклі, хоча ви можете перейти IEnumeratorлише з ним). Вони залишаються в пам'яті як є.
IQueryable це дерево виразів, яке в якийсь момент буде перетворене на щось інше з можливістю перерахувати над кінцевим результатом . Я думаю, саме це бентежить більшість людей.
Вони, очевидно, мають різні конотації.
IQueryableявляє собою дерево виразів (запит, просто), яке буде переведено на щось інше, що лежить в основі постачальника запитів, як тільки викликаються API випуску, наприклад, функції сукупності LINQ (сума, підрахунок тощо) або ToList [Array, Dictionary ,. ..]. І IQueryableоб'єкти також реалізують IEnumerable, IEnumerable<T>так що якщо вони представляють запит, результат цього запиту може бути повторений. Це означає, що IQueryable не повинен бути лише запитом. Правильний термін - це дерева виразів .
Тепер, як ці вирази виконуються і до чого вони звертаються, все залежить від так званих постачальників запитів (виконавців виразів, про які ми можемо їх думати).
У світі Entity Framework (це означає, що містичний базовий постачальник джерел даних або постачальник запитів) IQueryableвирази переводяться на рідні T-SQL запити. Nhibernateробить подібні речі з ними. Ви можете написати свій власний, керуючись поняттями, досить добре описаними в LINQ: Наприклад, створення посилання постачальника послуг IQueryable , і ви, можливо, захочете мати спеціальний API запиту для служби постачальника товарів.
Отже, IQueryable об'єкти будуються протягом усього часу, поки ми явно не звільнимо їх і не скажемо системі переписати їх у SQL або що завгодно і відправити ланцюжок виконання для подальшої обробки.
Начебто для відкладеного виконання - це LINQможливість утримувати схему дерева виразів у пам'яті та відправляти її у виконання лише на вимогу, коли виклики певних API проти послідовності (той самий Count, ToList тощо).
Правильне використання обох сильно залежить від завдань, які стоять перед вами у конкретному випадку. Для відомого шаблону репозиторію я особисто вибираю повернення IList, тобто IEnumerableнад списками (індексатори тощо). Тож IQueryableрадимо використовувати лише в сховищах і IE чимало в іншому місці коду. Не кажучи про проблеми, пов'язані з встановленням тестів, які IQueryableруйнуються та руйнують принцип поділу проблем . Якщо ви повернете вираз із сховищ, споживачі можуть грати зі стійким шаром, як хотіли б.
Невелике доповнення до безладу :) (з дискусії в коментарях)) Жоден з них не є об’єктами в пам'яті, оскільки вони самі по собі не є реальними типами, вони є маркерами типу - якщо ви хочете заглибитись так глибоко. Але має сенс (і тому навіть MSDN так вважає) думати про IEnumerables як колекції пам'яті, тоді як IQueryables - як дерева виразів. Справа в тому, що інтерфейс IQueryable успадковує інтерфейс IEnumerable, щоб, якщо він представляє запит, результати цього запиту можна було перерахувати. Перерахування спричиняє виконання експресійного дерева, пов'язаного з об'єктом IQueryable. Отже, насправді ви не можете дійсно зателефонувати жодному члену IEnumerable, не маючи об’єкта в пам'яті. Він потрапить туди, якщо ви все одно, якщо він не порожній. IQueryables - це лише запити, а не дані.