Перейти к содержанию
Educora
Продвинутый19 мин9 / 16

LINQ: запросы к данным

Фильтруй, преобразуй, сортируй и подсчитывай коллекции с помощью лямбда-выражений и методов LINQ: `Where`, `Select`, `OrderBy`, `Sum`, `Average`, `GroupBy`.

Проверь себя
В этом уроке ты узнаешь
  • Читать и писать лямбда-выражения (x => ...)
  • Фильтровать, преобразовывать и сортировать коллекцию с помощью Where, Select и OrderBy
  • Подводить итоги с помощью Sum, Average, Count, Any и GroupBy
  • Объяснять отложенное выполнение и роль ToList()

Нужно найти в списке класса учеников, набравших больше 80 баллов, отсортировать их по убыванию и взять только имена. С циклами это новые списки, условия if и код сортировки. С LINQ (Language Integrated Query — встроенный в язык запрос) та же работа занимает несколько понятных строк — словно ты просто говоришь компьютеру, что тебе нужно.

Лямбда-выражения

Определение
Лямбда-выражение

Короткая безымянная функция: слева от стрелки (=>) — параметр, справа — результат. n => n * 2 означает «возьми n, верни n · 2», а s => s.Score >= 80 отвечает true/false на вопрос «набрал ли ученик 80 баллов или больше?».

Методы LINQ работают с любой коллекцией — массивом, List<T>, словарём — и принимают лямбду в качестве аргумента. Они не меняют исходную коллекцию, а возвращают новый результат. В примере ниже Where отбирает элементы, подходящие под условие, Select преобразует каждый элемент, а OrderBy сортирует.

C#
int[] numbers = [5, 12, 8, 3, 20, 7];

var evens = numbers.Where(n => n % 2 == 0);
var doubled = numbers.Select(n => n * 2);
var sorted = numbers.OrderBy(n => n);

Console.WriteLine(string.Join(", ", evens));
Console.WriteLine(string.Join(", ", doubled));
Console.WriteLine(string.Join(", ", sorted));
Console.WriteLine($"Sum: {numbers.Sum()}, max: {numbers.Max()}");
Console.WriteLine($"Greater than 6: {numbers.Count(n => n > 6)}");
Ожидаемый результат
12, 8, 20
10, 24, 16, 6, 40, 14
3, 5, 7, 8, 12, 20
Sum: 55, max: 20
Greater than 6: 4

Цепочки методов

Поскольку каждый метод LINQ возвращает новую последовательность, на ней можно сразу вызвать следующий метод. Такая цепочка читается сверху вниз как предложение: «отбери тех, у кого 80 и больше, отсортируй по убыванию баллов, возьми имена». Цепочки обычно пишут так, чтобы каждый метод был на отдельной строке.

C#
using System.Globalization;

CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

List<Student> students =
[
    new("Aysel", 95), new("Murad", 78), new("Leyla", 88),
    new("Elvin", 45), new("Nigar", 91)
];

var topNames = students
    .Where(s => s.Score >= 80)
    .OrderByDescending(s => s.Score)
    .Select(s => s.Name);
Console.WriteLine(string.Join(", ", topNames));

Console.WriteLine($"Average: {students.Average(s => s.Score):F1}");
Console.WriteLine($"Everyone passed: {students.All(s => s.Score >= 50)}");
Console.WriteLine($"Anyone above 90: {students.Any(s => s.Score > 90)}");
Console.WriteLine($"Best: {students.OrderByDescending(s => s.Score).First().Name}");

record Student(string Name, int Score);
Ожидаемый результат
Aysel, Nigar, Leyla
Average: 79.4
Everyone passed: False
Anyone above 90: True
Best: Aysel
МетодЧто делает
Where(x => ...)отбирает элементы, подходящие под условие
Select(x => ...)превращает каждый элемент в другое значение
OrderBy / OrderByDescendingсортирует по возрастанию / убыванию
Sum, Average, Min, Max, Countсумма, среднее, минимум, максимум, количество
First / FirstOrDefaultдаёт первый элемент; если пусто — ошибка / null
Any / Allподходит ли хотя бы один / все элементы
GroupBy(x => ...)группирует элементы по ключу
ToList()превращает результат в готовый список

Группировка

GroupBy делит элементы на группы по общему ключу: например, товары по категориям. У каждой группы есть свойство Key (ключ), а сама группа — тоже коллекция, поэтому у неё можно вызвать Count и Sum. А FirstOrDefault, если подходящего элемента нет, не выбрасывает ошибку, а возвращает null.

C#
List<Product> products =
[
    new("Laptop", "Electronics", 1500),
    new("Pen", "Stationery", 2),
    new("Phone", "Electronics", 900),
    new("Notebook", "Stationery", 5),
    new("Mouse", "Accessories", 25)
];

var groups = products
    .GroupBy(p => p.Category)
    .OrderBy(g => g.Key);

foreach (var g in groups)
    Console.WriteLine($"{g.Key}: {g.Count()} item(s), total {g.Sum(p => p.Price)} AZN");

Product? cheap = products.FirstOrDefault(p => p.Price < 1);
Console.WriteLine(cheap is null ? "No product under 1 AZN" : cheap.Name);

record Product(string Name, string Category, decimal Price);
Ожидаемый результат
Accessories: 1 item(s), total 25 AZN
Electronics: 2 item(s), total 2400 AZN
Stationery: 2 item(s), total 7 AZN
No product under 1 AZN
Вопросительный знак в типе Product? означает, что переменная может быть null.
Интерактив
Загрузка симуляции…
OrderBy избавляет от необходимости самому писать алгоритм сортировки. Здесь можно посмотреть, как шаг за шагом работает один из простых алгоритмов.

Отложенное выполнение

При вызове Where и Select ещё ничего не вычисляется — запоминается только «рецепт». Запрос выполняется, когда результат действительно нужен: в foreach, в string.Join или в методах вроде Sum. Это называется отложенным выполнением. Чтобы «заморозить» результат в данный момент, вызови ToList().

C#
List<int> scores = [50, 70, 90];

var high = scores.Where(s => s > 60);
var snapshot = scores.Where(s => s > 60).ToList();

scores.Add(100);

Console.WriteLine(string.Join(", ", high));
Console.WriteLine(string.Join(", ", snapshot));
Ожидаемый результат
70, 90, 100
70, 90
Запрос high выполнился уже после добавления 100, а snapshot был построен заранее.

Главное

  • Лямбда — короткая безымянная функция: n => n * 2.
  • Where фильтрует, Select преобразует, OrderBy/OrderByDescending сортируют.
  • Sum, Average, Max, Count, Any и All дают один ответ по всей коллекции.
  • LINQ не меняет исходную коллекцию; методы можно соединять в цепочки.
  • Запрос выполняется, когда запрашивают результат; ToList() строит результат сразу.

Проверь себя

Вопросов: 10. Каждый правильный ответ приносит XP.

1 / 10
Дано int[] a = [1, 2, 3, 4];. Чему равно a.Where(n => n > 2).Sum()?