Многое было сказано ранее, но вернемся к корням более техническим способом:
IEnumerable это коллекция объектов в памяти, которую вы можете перечислить - последовательность в памяти, которая позволяет выполнять итерации (упрощает foreachцикл в цикле, хотя вы можете работать IEnumeratorтолько с ним). Они хранятся в памяти как есть.
IQueryable это дерево выражений , которое в какой-то момент преобразуется во что-то другое с возможностью перечисления окончательного результата . Я думаю, это то, что смущает большинство людей.
Они, очевидно, имеют разные значения.
IQueryableпредставляет собой дерево выражений (просто запрос), которое будет преобразовано во что-либо еще базовым поставщиком запросов при вызове API-интерфейсов выпуска, например агрегатных функций LINQ (Sum, Count и т. д.) или 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только в репозиториях и IEnumerable где-либо еще в коде. Не говоря о проблемах, связанных с проверяемостью, которые IQueryableразрушают и разрушают принцип разделения интересов . Если вы возвращаете выражение из репозиториев, потребители могут поиграть со слоем персистентности так, как им хотелось бы.
Небольшое дополнение к беспорядку :) (из обсуждения в комментариях)) Ни один из них не является объектами в памяти, так как они не являются реальными типами как таковыми, это маркеры типа - если вы хотите углубиться в это. Но имеет смысл (и именно поэтому даже MSDN так выразился) рассматривать IEnumerables как коллекции в памяти, а IQueryables - как деревья выражений. Дело в том, что интерфейс IQueryable наследует интерфейс IEnumerable, так что, если он представляет запрос, результаты этого запроса могут быть перечислены. Перечисление вызывает выполнение дерева выражений, связанного с объектом IQueryable. Таким образом, на самом деле вы не можете вызвать любой член IEnumerable, не имея объекта в памяти. В любом случае он попадет туда, если вы это сделаете, если он не пустой. IQueryables - это просто запросы, а не данные.