Ответ
Отложенное (ленивое) выполнение означает, что запрос не выполняется в момент его объявления. Вычисление откладывается до момента фактического перечисления результатов (итерирования). Немедленное выполнение приводит к немедленному вычислению и материализации результата.
Пример, демонстрирующий разницу:
// 1. СОЗДАНИЕ ЗАПРОСА (Отложенное выполнение)
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var deferredQuery = numbers.Where(n => n % 2 == 0); // Запрос ОПРЕДЕЛЕН, но НЕ ВЫПОЛНЕН
numbers.Add(6); // Изменяем исходную коллекцию
// 2. ВЫПОЛНЕНИЕ ЗАПРОСА (происходит здесь)
foreach (var num in deferredQuery) // Запрос выполняется в момент итерации
{
Console.Write(num + " "); // Вывод: 2 4 6
}
// Запрос "увидел" добавленную 6, потому что выполнился после изменения.
// 3. НЕМЕДЛЕННОЕ ВЫПОЛНЕНИЕ
var immediateResult = numbers.Where(n => n > 3).ToList(); // Запрос выполняется и материализуется в список СРАЗУ
// immediateResult теперь содержит [4, 5, 6] и больше не зависит от изменений в numbers.
Ключевые различия и практические следствия:
| Критерий | Отложенное выполнение | Немедленное выполнение |
|---|---|---|
| Время выполнения | При перечислении (foreach, ToList). |
В точке вызова метода материализации. |
| Повторное выполнение | Запрос выполняется заново при каждом перечислении. | Результат вычислен один раз и сохранен. |
| Актуальность данных | Использует самые свежие данные на момент перечисления. | Фиксирует "снимок" данных на момент вызова. |
| Типичные операторы | Where, Select, OrderBy (возвращают IEnumerable<T>/IQueryable<T>). |
ToList, ToArray, Count, First, Any (возвращают конкретное значение или коллекцию). |
Зачем это нужно? Отложенное выполнение позволяет строить сложные, композируемые запросы поэтапно и выполняет их оптимально одним разом, что особенно важно для IQueryable<T> и запросов к базе данных.