JavaRush /Курсы /C# SELF /Фильтрация данных с помощью

Фильтрация данных с помощью Where

C# SELF
31 уровень , 2 лекция
Открыта

1. Введение

Фильтрация — это выборка из коллекции только тех элементов, которые отвечают определённому условию. Представьте: в вашей базе — 10 000 товаров, а менеджеру нужно «только» 12, которые подходят под новый хитрый критерий. Писать каждый раз вручную перебор и кучу проверок — удовольствие сомнительное, а код становится нечитаемым. LINQ берёт эти хлопоты на себя.

В реальных приложениях фильтрация — самая частая операция с данными: показываем пользователю только те записи, которые ему интересны, «чистим» коллекцию от нежелательных элементов, делаем выборки для отчётов, поиска или отправки e-mail. На собеседованиях вопросы о LINQ-фильтрации встречаются постоянно. Понимание этой темы — залог успеха для начинающего .NET-разработчика.

2. Знакомимся с методом Where

Смысл и принцип работы

Метод Where — это расширяющий метод LINQ, который принимает функцию (или лямбда-выражение), определяющую условие фильтрации. Он возвращает новую последовательность (не изменяя исходную!), в которой содержатся только те элементы, для которых условие возвращает true.

Сигнатура основная:

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate)

Не пугайтесь generics и страшных слов. Сейчас важно понять общую идею: Where принимает исходную коллекцию и условие, которое вы задаете. На практике всё просто:

  • source — та коллекция, которую фильтруем (например, List<Product>)
  • predicate — функция/условие (например, p => p.Price > 100)

Простая задача для затравки

Допустим, у нас есть список товаров, и мы хотим выбрать только те, у которых цена больше 100. В старые-добрые времена писали бы цикл foreach и вручную добавляли нужные элементы в новый список. LINQ позволяет сделать это одной строкой.

3. Фильтрация

Наш класс Product и стартовая коллекция

В прошлом модуле мы писали примерно такой класс товаров:


class Product
{
    public string Name { get; set; }
    public double Price { get; set; }

    // Для красоты вывода на экран:
    public override string ToString()
    {
        return $"{Name} (цена: {Price})";
    }
}

Допустим, у нас есть список товаров:


var products = new List<Product>
{
    new Product { Name = "Хлеб", Price = 30 },
    new Product { Name = "Молоко", Price = 87 },
    new Product { Name = "Сыр", Price = 250 },
    new Product { Name = "Шоколад", Price = 130 }
};

Фильтрация товаров дороже 100 с помощью Where


var expensiveProducts = products.Where(p => p.Price > 100);

foreach (var product in expensiveProducts)
{
    Console.WriteLine(product);
}

Что происходит под капотом:

  • Where перебирает все элементы списка products,
  • Для каждого вызывает функцию (в нашем случае: p => p.Price > 100),
  • Если функция вернула true, товар попадает в новую коллекцию.

Результат на экране:

Сыр (цена: 250)
Шоколад (цена: 130)

Заметьте: исходный список products не изменился! LINQ не разрушает ваши данные.

4. Ленивая фильтрация (Deferred Execution)

Интересный и важный момент: результат работы Where вычисляется, только когда вы реально обращаетесь к элементам. Например, как только начинается цикл foreach, LINQ «идёт» по исходной коллекции и фильтрует элементы налету. Если вы не используете результат, никакой работы не происходит. Это называется отложенное выполнение (deferred execution).

Это даёт нам кучу плюсов:

  • Можно строить очень длинные цепочки методов (например, и отфильтровать, и отсортировать), и только при первом реальном запросе к результату всё выполняется.
  • Экономия памяти: не происходит промежуточных лишних преобразований коллекций.
  • При необходимости можно отменить выполнение или «прервать» после нужного количества найденных элементов.

Визуальная схема


Исходная коллекция       —►   Where (условие)    —►   Перебираем только нужные элементы
    
[ х ] [ х ] [ + ] [ + ]  —►   p.Price > 100      —►   [ + ] [ + ]

(х — элемент не удовлетворяет условию, + — удовлетворяет)

5. Синтаксические варианты

Классический метод расширения (Method Syntax)

Этот стиль мы уже используем:

var result = products.Where(p => p.Price > 100);

Альтернатива: Query Syntax

LINQ поддерживает синтаксис, похожий на SQL-запросы:


var result = from p in products
             where p.Price > 100
             select p;

Результат — тот же!
Выбор между синтаксисом — дело вкуса, но method syntax (точка-методы) встречается чаще, особенно в реальных проектах и при построении длинных цепочек.

6. Сложные условия фильтрации

Несколько условий

Вы можете использовать логические операторы (&&, ||, !):


// Найти все дорогие товары, кроме "Шоколад"
var filtered = products.Where(
    p => p.Price > 100 && p.Name != "Шоколад"
);

Фильтрация по строке (подстрока, регистр, Contains)


// Товары, в названии которых есть буква "л"
var lProducts = products.Where(p => p.Name.Contains("л"));

Обратите внимание: Contains чувствителен к регистру! Если нужен «без учета регистра», можно так:


var lProducts = products.Where(p => p.Name
    .ToLower().Contains("л")); // но теперь "Молоко" и "Шоколад" оба попадут в выборку

Фильтрация по нескольким коллекциям

Допустим, у вас есть два списка — товары и платежи. Можно фильтровать товары, которые встречаются в списке платежей — но для этого лучше использовать методы Any и Join, о которых будем говорить дальше. Просто знайте: LINQ умеет фильтровать и более сложными способами!

7. Полезные нюансы

Вложенная фильтрация и цепочки методов

Вы можете объединять несколько этапов фильтрации, вызывая Where несколько раз подряд:


var filtered = products
    .Where(p => p.Price > 100)
    .Where(p => p.Name.StartsWith("Ш"));

Однако лучше объединять условия в один Where с помощью логических операторов — так эффективнее и код проще.

Встроенные компараторы и кастомные функции

Иногда стандартных операторов мало. Например, нужно фильтровать строки без учета регистра и с учетом культуры. В этом случае удобно использовать методы сравнения:


var rusProducts = products.Where(
    p => p.Name.StartsWith("ш", StringComparison.OrdinalIgnoreCase)
);

8. Фильтрация и пользовательский ввод

Попробуем сделать простую командную фильтрацию на практике! Например, спросим у пользователя минимальную цену, потом покажем все подходящие товары.


Console.Write("Минимальная цена товара? ");
var minPriceStr = Console.ReadLine();
if (double.TryParse(minPriceStr, out double minPrice))
{
    var filtered = products.Where(p => p.Price >= minPrice);
    foreach (var product in filtered)
    {
        Console.WriteLine(product);
    }
}
else
{
    Console.WriteLine("Ошибка: введено не число.");
}

Теперь наше "мини-приложение" уже почти выглядит как магазин!

9. Типичные ошибки и грабли при использовании Where

В программировании, как и в жизни, встречаются подводные камни. Вот чего стоит опасаться.

Забыли вызвать ToList() или ToArray()

Результат работы Where — это не список и даже не массив! Это объект IEnumerable<Product>. Чаще всего он прекрасно работает в цикле foreach, но если нужна именно коллекция (например, если планируете индексированный доступ), не забудьте вызвать .ToList() или .ToArray():


var filteredList = products.Where(p => p.Price > 100).ToList();

Если игнорировать этот момент, легко получить ошибку вроде "Collection was modified during enumeration" — особенно если вы решите изменить исходную коллекцию во время перебора.

Изменение исходной коллекции

Поскольку LINQ-фильтрация отложенная, если в середине перебора вы удаляете элементы из исходного списка, может возникнуть исключение. Особенно это касается многопоточных сценариев или случаев, когда коллекция меняется прямо в процессе фильтрации.

Null-значения и предикаты

Если коллекция содержит null, а вы в предикате обращаетесь к полю объекта без проверки, получите NullReferenceException. Например:


var filtered = products.Where(p => p.Name.StartsWith("А"));

Если какой-то элемент в products равен null, код сломается.

Рекомендация: всегда добавляйте проверку на null, если есть риск наличия таких элементов:


var filtered = products.Where(p => p != null && p.Name.StartsWith("А"));
2
Задача
C# SELF, 31 уровень, 2 лекция
Недоступна
Фильтрация объектов по нескольким критериям
Фильтрация объектов по нескольким критериям
Комментарии (2)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Slevin Уровень 57
11 февраля 2026
Нужно больше задач. Да, всегда можно потом пойти в курс по Postgres и там потренироваться, но хотелось бы тренироваться в C#/.NET
Кирилл Уровень 61
17 ноября 2025
в примере с Contains Если нужен «без учета регистра» // но теперь "Молоко" и "Шоколад" оба попадут в выборку наверно лучше добавить товар который начинается с буквы 'Л'(Лимон например) ибо и молоко и шоколад содержат 'л' в нижнем регистре, 'л' также содержит и "Хлеб"