В чем разница между IEnumerable и IQueryable в C#?

«В чем разница между IEnumerable и IQueryable в C#?» — вопрос из категории C# Core, который задают на 51% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Разница заключается в месте выполнения запроса и, как следствие, в его эффективности.

Характеристика IEnumerable<T> IQueryable<T>
Пространство имен System.Collections System.Linq
Цель Работа с коллекциями в памяти (LINQ to Objects). Работа с внешними источниками данных (LINQ to Entities, LINQ to SQL).
Место выполнения Запрос выполняется на стороне клиента (в памяти приложения). Запрос транслируется в язык источника (например, SQL) и выполняется на стороне сервера (БД).
Философия «Принеси все данные, потом отфильтруй». «Расскажи, что нужно, я сам отфильтрую и принесу результат».

Наглядный пример с Entity Framework Core:

// Контекст базы данных
var dbContext = new AppDbContext();

// ПЛОХО: Использование IEnumerable
IEnumerable<Product> productsEnum = dbContext.Products; // Неявное приведение
var cheapProductsEnum = productsEnum.Where(p => p.Price < 50).ToList();
// Что происходит:
// 1. `dbContext.Products` выполняется как SQL: SELECT * FROM Products
// 2. ВСЕ продукты загружаются в память приложения.
// 3. Фильтр `Where(p => p.Price < 50)` применяется в памяти к уже загруженным данным.

// ХОРОШО: Использование IQueryable
IQueryable<Product> productsQuery = dbContext.Products;
var cheapProductsQuery = productsQuery.Where(p => p.Price < 50).ToList();
// Что происходит:
// 1. Запрос НЕ выполняется сразу. Строится выражение.
// 2. При вызове `.ToList()` выражение транслируется в SQL:
//    SELECT * FROM Products WHERE Price < 50
// 3. В память загружаются ТОЛЬКО отфильтрованные данные.

Ключевые выводы:

  • Используйте IQueryable, когда работаете с ORM (EF Core) и хотите, чтобы фильтрация, сортировка и пагинация выполнялись на стороне БД. Это критически важно для производительности.
  • Используйте IEnumerable, когда источник данных уже находится в памяти (массив, список) или после того, как вы материализовали IQueryable (вызвали .ToList(), .ToArray()).
  • Материализация: Вызов методов, возвращающих коллекции (.ToList(), .First()), или перечисление через foreach приводит к выполнению запроса и переходу от IQueryable к IEnumerable.