← Назад к вопросам

Какой тип объекта возвращается при использовании Where?

1.0 Junior🔥 271 комментариев
#Коллекции и структуры данных#Основы C# и .NET

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Тип возвращаемого значения метода Where в C#

При использовании метода расширения Where из пространства имён System.Linq возвращается специальный тип, реализующий интерфейс IEnumerable<T>, где T — тип элементов исходной коллекции. Конкретный тип зависит от контекста вызова и исходного источника данных.

Основные варианты возвращаемых типов

1. IEnumerable<T> для коллекций в памяти

При вызове Where на стандартных коллекциях в памяти (таких как List<T>, T[], Collection<T> и т.д.) возвращается экземпляр внутреннего класса System.Linq.Enumerable.WhereArrayIterator<T> или System.Linq.Enumerable.WhereListIterator<T>.

using System.Linq;

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var filtered = numbers.Where(n => n > 3); // Тип: WhereListIterator<int>
Console.WriteLine(filtered.GetType().Name); // Вывод: WhereListIterator`1

2. IQueryable<T> для запросов к источникам данных

При работе с LINQ to Entities, LINQ to SQL или другими поставщиками LINQ, которые реализуют IQueryable<T>, метод Where возвращает IQueryable<T>. Это позволяет строить деревья выражений (Expression Trees) для последующей трансляции в язык запросов (например, SQL).

using System.Linq;

IQueryable<Product> products = dbContext.Products;
var query = products.Where(p => p.Price > 100); // Тип: IQueryable<Product>

3. Отложенное выполнение (Deferred Execution)

Важно понимать, что Where возвращает не материализованную коллекцию, а запрос, который будет выполнен только при итерации (например, при вызове ToList(), ToArray() или в цикле foreach). Это называется отложенным выполнением.

var query = numbers.Where(n => n % 2 == 0); // Запрос ещё не выполнен
var result = query.ToList(); // Здесь происходит выполнение и материализация

Ключевые особенности возвращаемого объекта

  • Ленивое вычисление: Элементы фильтруются "на лету" при итерации.
  • Композиция запросов: Методы LINQ можно цепочко соединять, создавая сложные запросы.
  • Иммутабельность: Исходная коллекция остаётся неизменной.
  • Оптимизация: Для IQueryable<T> условия фильтрации компилируются в выражение и могут транслироваться в оптимизированный SQL.

Пример с цепочкой вызовов

var processedData = sourceCollection
    .Where(item => item.IsActive)          // Возвращает IEnumerable<T>
    .OrderBy(item => item.Date)            // Возвращает IOrderedEnumerable<T>
    .Select(item => item.ToViewModel());   // Возвращает IEnumerable<TViewModel>

Когда происходит материализация?

Возвращаемый IEnumerable<T> из Where остаётся "запросом" до явной материализации:

var filtered = numbers.Where(n => n > 10); // Только определение запроса
var list = filtered.ToList();              // Выполнение и материализация
var array = filtered.ToArray();            // Альтернативная материализация

Отличие IEnumerable<T> от IQueryable<T>

  • IEnumerable<T>: Фильтрация происходит в памяти с использованием делегатов (Func<T, bool>).
  • IQueryable<T>: Фильтрация строится как дерево выражений (Expression<Func<T, bool>>) для внешнего выполнения (например, в СУБД).
// IEnumerable<T> - выполнение в памяти
IEnumerable<Product> inMemory = productsList.Where(p => p.Price > 100);

// IQueryable<T> - преобразование в SQL
IQueryable<Product> fromDatabase = dbContext.Products.Where(p => p.Price > 100);

Таким образом, тип объекта, возвращаемого Where, всегда является производным от IEnumerable<T> или IQueryable<T>, что обеспечивает единообразный подход к работе с данными в LINQ, поддерживая как отложенное выполнение, так и композицию запросов.

Какой тип объекта возвращается при использовании Where? | PrepBro