| layout | default | |||
|---|---|---|---|---|
| title | 📚 В чём разница между `IEnumerable<T>` и `IQueryable<T>`? | |||
| description | ||||
| author | Dvurechensky | |||
| date | 2025-08-28 | |||
| published | true | |||
| tags |
|
- ✨ Оглавление
-
Определён в
System.Collections.Generic. -
Позволяет перебирать коллекцию элементов в памяти (in-memory).
-
Поддерживает только LINQ to Objects.
-
Выполнение немедленное только при обращении к коллекции (
foreach). -
Методы расширения LINQ для
IEnumerable<T>:Where,Select,OrderBy,Take,Skip.
-
Отложенное исполнение (
deferred execution) — да, но только в памяти.
-
Определён в
System.Linq. -
Расширяет
IEnumerable<T>:- Добавляет
Expressionдля построения дерева выражений.
- Добавляет
-
Используется для удалённого источника данных (например, БД через Entity Framework, LINQ to SQL).
-
LINQ-запросы не выполняются сразу; создаётся дерево выражений.
-
Выполнение (
execution) происходит только при материализации данных (ToList(),ToArray(),foreach).
-
IEnumerable
-
Отложенное выполнение выполняется после того, как объект в памяти доступен.
-
Пример:
var query = list.Where(x => x > 5); // ещё не выполняется foreach(var n in query) { ... } // выполняется здесь
-
-
IQueryable
-
Отложенное выполнение формирует SQL-запрос (или другой провайдерский запрос).
-
Пример EF:
var query = db.Users.Where(u => u.Age > 18); // строится Expression tree var result = query.ToList(); // выполняется SQL-запрос
-
| Характеристика | IEnumerable | IQueryable |
|---|---|---|
| Источник данных | В памяти (объекты .NET) | Внешний источник (БД, веб-сервис) |
| SQL/провайдер | Нет | Да, формирует запрос к источнику |
| LINQ методы | Все выполняются в памяти | Выражения преобразуются в Expression Tree → выполняются удалённо |
| Поддержка сложных запросов | Ограничена (в памяти) | Полная, если провайдер поддерживает |
List<int> numbers = new() {1,2,3,4,5};
var query = numbers.Where(n => n > 2).Select(n => n * 2);
foreach(var n in query) Console.WriteLine(n); // выполняется в памяти- Все элементы уже в памяти.
WhereиSelect— C# делегаты.
var query = db.Users
.Where(u => u.Age > 18)
.Select(u => new { u.Name, u.Age });
var result = query.ToList(); // SQL запрос к БД выполняется здесьWhereиSelect→ выражения, переводятся в SQL.- Эффективно: фильтруются только нужные данные.
-
IEnumerable
- Когда данные уже загружены в память.
- Для небольших коллекций и локальных операций.
-
IQueryable
- Когда нужно формировать запрос к БД или удалённым источникам.
- Для больших данных, чтобы фильтровать/проецировать на стороне сервера.
-
IEnumerable<T>:- Перебор всех элементов → O(n).
- Если фильтруешь много данных → всё уже загружено в память → дорого.
-
IQueryable<T>:- Генерирует оптимальный запрос к серверу.
- Обрабатывает фильтрацию на стороне источника → экономия памяти и сети.
-
Подводный камень:
- Частое комбинирование
IEnumerable<T>иIQueryable<T>может вынести всю коллекцию в память раньше времени.
- Частое комбинирование
-
Что происходит при смешении
IEnumerable<T>иIQueryable<T>?var result = db.Users.AsEnumerable().Where(u => u.Age > 18);
AsEnumerable()переводитIQueryableвIEnumerable→ дальнейшие фильтры выполняются в памяти, а не в SQL.
-
Deferred execution
- Часто спрашивают, когда именно выполняется запрос.
Ответ: только при фактической итерации (
foreach,ToList(),Count(),First()).
- Часто спрашивают, когда именно выполняется запрос.
Ответ: только при фактической итерации (
-
Что такое Expression Tree?
- В
IQueryableкаждый метод LINQ создаётExpression→ можно преобразовать в SQL или другой провайдерский код.
- В
-
Можно ли использовать сложные функции C# в
IQueryable?- Нет, только то, что провайдер может транслировать (например, EF Core может не поддерживать все методы).
-
Память
IEnumerableработает в памяти → нагрузка на RAM.IQueryableможет использовать отложенную выборку → меньше памяти.
-
Вызовы методов
IEnumerable<T>— методы LINQ вызываются в C# коде.IQueryable<T>— методы формируют дерево выражений → выполняются в источнике данных.
// IQueryable -> SQL
var query1 = db.Users.Where(u => u.Name.StartsWith("A"));
// SQL генерируется при ToList()
// IEnumerable -> в памяти
var query2 = db.Users.ToList().Where(u => u.Name.StartsWith("A"));
// Все пользователи загружены, фильтрация в памятиquery1— эффективнее.query2— грузим всё в память → неэффективно для больших таблиц.
| Характеристика | IEnumerable | IQueryable |
|---|---|---|
| Namespace | System.Collections.Generic | System.Linq |
| Основа | Делегаты C# | Expression Tree |
| Источник данных | In-memory | Remote / DB / External |
| Deferred execution | Да | Да, с генерацией выражения |
| SQL / Provider translation | Нет | Да |
| Преобразования | LINQ to Objects | LINQ to Entities / LINQ to SQL |
| Производительность | Может быть медленнее для больших данных | Оптимизировано для источника |
| Поддержка методов C# | Все | Только методы, поддерживаемые провайдером |
| Аллокация памяти | Вся коллекция | Только нужные элементы |
✨Dvurechensky✨