JavaRush /Курсы /C# SELF /Введение в LINQ и его преимущества

Введение в LINQ и его преимущества

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

1. Введение

Представьте, что вы приходите в библиотеку, и вам нужно найти все книги по программированию, изданные после 2020 года, отсортированные по имени автора. Вряд ли вы бы стали бегать от полки к полке и проверять каждую книгу вручную, верно? Вы бы попросили библиотекаря, который знает, как быстро найти то, что вам нужно.

LINQ (Language Integrated Query, или "Запросы, интегрированные в язык") – это наш "умный библиотекарь", "SQL-движок" (то есть, инструмент для написания запросов к данным) прямо внутри C#!

Думайте о LINQ как о языке, который позволяет вам описывать, что вы хотите получить от данных, а не как это сделать. Вместо того, чтобы требовать: "Возьми первый элемент, проверь условие, если подходит, добавь в новый список, потом переходи ко второму...", вы просто говорите: "Дай мне все продукты, цена которых выше 1000". А C# сам разберется, как это выполнить наиболее эффективно.

Ключевая идея LINQ: предоставить стандартный синтаксис для запросов к любым источникам данных, которые реализуют интерфейс IEnumerable<T>. И это замечательно, потому что List<T>, массивы, HashSet<T> – все они реализуют IEnumerable<T>. А если ваши данные в базе, то специальные библиотеки (например, Entity Framework) преобразуют ваши LINQ-запросы в настоящий SQL!

LINQ появился в C# более 10 лет назад. Это было знаковое событие, которое изменило подход к работе с данными в .NET. До LINQ разработчикам приходилось писать много шаблонного кода для фильтрации, сортировки и трансформации коллекций. Или использовать строки SQL-запросов, а они не проверялись компилятором и были подвержены ошибкам времени выполнения. LINQ принес концепцию "запросов" прямо в язык программирования, сделав их типобезопасными и более читаемыми.

Многие считают, что LINQ — это одно из самых значительных нововведений в C# со времен появления обобщений (Generics).

2. Преимущества LINQ: Почему его все любят?

Краткость и читаемость кода: Чтобы отфильтровать, например, продукты с ценой выше 1000 в большой базе, приходилось писать несколько строк кода. С LINQ это можно выразить в одной. Меньше кода — меньше потенциальных ошибок, выше читаемость и понятность. Код становится ближе к естественному языку.
Шутка программиста: "Чем меньше кода я пишу, тем меньше багов я могу в него засунуть." LINQ помогает в этом!

Мощность и гибкость: LINQ предоставляет богатый набор операций: фильтрация (Where), проекция (Select), сортировка (OrderBy), группировка (GroupBy), агрегация (Sum, Average), объединение (Join) и многое другое. Он позволяет решать сложные задачи обработки данных, комбинируя эти операции.

Типобезопасность: Это очень важно! Когда вы пишете SQL-запрос в виде строки, компилятор ничего о нем не знает. Если вы ошиблись в названии столбца, то узнаете об этом только во время выполнения программы, когда она упадет с ошибкой. С LINQ компилятор C# проверяет ваш запрос на ошибки прямо во время компиляции. Если вы попытаетесь запросить несуществующее поле у объекта Product, компилятор тут же покажет ошибку. Это как иметь персонального корректора, который ловит ваши опечатки до того, как их увидят другие.

Интеграция с языком: Запросы LINQ – это не какие-то "магические" строки. Это полноценные конструкции C#, которые используют знакомые вам лямбда-выражения и работают с уже знакомыми типами данных. Это делает переход к LINQ очень плавным.

Отложенное выполнение (Deferred Execution): Это одна из самых крутых и, возможно, самых сложных концепций LINQ, но очень важная. Суть в том, что LINQ-запрос не выполняется сразу, как только вы его написали. Он "собирается" и ждет, пока вы действительно не попросите результат (например, начнете перебирать его в foreach). Это позволяет оптимизировать запросы, особенно при работе с большими объемами данных. Мы поговорим об этом более подробно в Лекции 166. Пока просто держите в уме: LINQ умный, он не делает лишнюю работу.

Универсальность и расширяемость: LINQ — это не только для списков в памяти. Существуют различные "провайдеры" LINQ:

  • LINQ to Objects: для коллекций в памяти (List<T>, массивы и т.д.).
  • LINQ to SQL / Entity Framework Core: для баз данных SQL (ваши запросы LINQ преобразуются в SQL-запросы).
  • LINQ to XML: для работы с XML-документами.
  • И многие другие.
    Это означает, что, освоив LINQ, вы сможете работать с данными из самых разных источников, используя одну и ту же логику запросов.

3. Проблема работы с коллекциями "по-старинке"

До появления LINQ код обработки коллекций в C# выглядел примерно так:


var products = new List<Product> { /* ... */ };

var expensive = new List<Product>();
foreach (var prod in products)
{
    if (prod.Price > 100)
        expensive.Add(prod);
}

expensive.Sort((a, b) => a.Price.CompareTo(b.Price));

foreach (var item in expensive)
    Console.WriteLine(item.Name);

Такой подход типичен: сначала вручную отфильтровать, потом отсортировать — обязательно создать промежуточный список. Чем больше логики, тем больше кода, ошибок и переменных. Всё это напоминает мастерскую по сборке мебели: детали есть, но процесс сборки — ручной и утомительный.

Алиса в стране циклов

Представьте, что у вас есть огромный список пользователей, и вы хотите получить только имена тех, кто старше 18 и живёт в городе Неонвиль, отсортировать их по алфавиту и вывести три первых. Вам придётся сочинять вложенные циклы, условия, сортировки, вспомогательные списки... или просто использовать LINQ.

4. Как выглядит LINQ-запрос? Простые примеры

LINQ предоставляет два основных синтаксиса:

  • Method Syntax (метод-цепочка)
  • Query Syntax (язык-подобный SQL)

Чаще всего используют Method Syntax, особенно в современных проектах. Вот пример на базе нашего приложения:


var expensive = products
    .Where(p => p.Price > 100)
    .OrderBy(p => p.Price)
    .Select(p => p.Name)
    .Take(3);

foreach (var name in expensive)
    Console.WriteLine(name);

Всё наглядно: фильтр, сортировка, выбор конкретного поля, ограничение количества. Минимум кода — максимум смысла.

То же самое через Query Syntax


var expensive = from p in products
                where p.Price > 100
                orderby p.Price
                select p.Name;

foreach (var name in expensive.Take(3))
    Console.WriteLine(name);

Оба варианта дают один и тот же результат — выбирайте тот, что больше по душе (но method-синтаксис всё же чаще используется).

5. Архитектура LINQ: что под капотом?

LINQ работает с любыми коллекциями, которые реализуют интерфейс IEnumerable<T> (или IQueryable<T>, о нём поговорим позднее).

Основные компоненты LINQ

Компонент Краткое описание
Классы-расширения LINQ Статические методы (
Where
,
Select
,
OrderBy
и др.) в классе
System.Linq.Enumerable
Делегаты Чаще всего используются
Func<T, TResult>
и
Predicate<T>
Отложенное выполнение Запрос вычисляется только при первом переборе (например, через
foreach
)
Query Provider (для LINQ to SQL, LINQ to Entities) — преобразует цепочку методов в SQL-запросы и т.п.

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


Коллекция (List<Product>, T[], ...) 
      │
      ▼
LINQ-методы (Where, OrderBy, Select...)
      │
      ▼
Запрос (IEnumerable<T>) 
      │
      ▼
Реальное выполнение (foreach, ToList, Count, ...)

6. Пример: пошаговый разбор LINQ-цепочки в нашем приложении

Возвращаемся к нашему мини-приложению с классом Product:


// Класс продукта
class Product
{
    public string Name { get; set; }
    public double Price { get; set; }
}

Вот набор продуктов:


var products = new List<Product>
{
    new Product { Name = "Сыр", Price = 250.5 },
    new Product { Name = "Хлеб", Price = 30 },
    new Product { Name = "Молоко", Price = 80 },
    new Product { Name = "Кофе", Price = 330 },
    new Product { Name = "Масло", Price = 140 }
};

Допустим, наша задача состоит в следующем: вывести на экран названия всех товаров дороже 100 евро, отсортировать по цене.

Старый способ


var filtered = new List<Product>();
foreach (var p in products)
{
    if (p.Price > 100)
        filtered.Add(p);
}

filtered.Sort((a, b) => a.Price.CompareTo(b.Price));

foreach (var p in filtered)
    Console.WriteLine(p.Name);

LINQ-способ


var expensive = products
    .Where(p => p.Price > 100)
    .OrderBy(p => p.Price)
    .Select(p => p.Name);

foreach (var name in expensive)
    Console.WriteLine(name);

В первой строке мы описываем саму суть задачи: "Отфильтруй те, у кого Price > 100, отсортируй по цене, выбери имена".

7. Частые операции LINQ и их аналоги "по-старинке"

Операция Логика "по-старинке" LINQ
Фильтрация foreach + if + Add
Where
Проекция (выбор поля) foreach + Add(field)
Select
Сортировка Sort(comparer)
OrderBy
,
OrderByDescending
Уникальные значения foreach + contains + Add
Distinct
Подсчёт foreach + counter++
Count
,
Count(predicate)
Проверка условия foreach + if
Any
,
All
Поиск первого/последнего foreach + if + break
First
,
Last
,
FirstOrDefault
Ограничение количества foreach + счетчик + break
Take
,
Skip

8. Практика: добавляем LINQ в приложение

Возьмём базовый код нашего приложения (Product-лист) и попробуем реализовать несколько полезных операций с помощью LINQ.

Фильтрация и сортировка


// Выбрать продукты дешевле 200 евро и отсортировать по имени
var cheapProducts = products
    .Where(p => p.Price < 200)
    .OrderBy(p => p.Name);

foreach (var p in cheapProducts)
    Console.WriteLine($"{p.Name}: {p.Price} евро");

Преобразование (проекция) коллекции


// Получить список цен (double)
var prices = products.Select(p => p.Price);

foreach (var price in prices)
    Console.WriteLine(price);

Поиск первого подходящего элемента


// Первый продукт, название которого начинается на "К"
var firstK = products.FirstOrDefault(p => p.Name.StartsWith("К"));
Console.WriteLine(firstK?.Name ?? "Не найдено");

Подсчет количества товаров дороже 100 евро


int count = products.Count(p => p.Price > 100);
Console.WriteLine($"Таких товаров: {count} шт.");

Начиная с этой лекции мы будем активно использовать LINQ для обработки коллекций, фильтрации, выбора нужных данных, агрегаций и многого другого. Новые методы и возможности LINQ (в том числе новинки .NET 9!) мы разберём в ближайших лекциях, а пока — не стесняйтесь экспериментировать с простыми запросами, чтобы почувствовать всю силу этого инструмента!

2
Задача
C# SELF, 31 уровень, 0 лекция
Недоступна
Фильтрация чисел из массива
Фильтрация чисел из массива
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Ra Уровень 35 Student
8 декабря 2025
Наконец-то что совсем новое по справнению с Java :) не считая делегатов, но они похожи на функциональные интерфейсы