Что такое LINQ в C#?

Ответ

LINQ (Language Integrated Query) — это набор технологий в C#, интегрирующих возможности запросов непосредственно в язык. Он предоставляет единый, декларативный синтаксис для работы с данными из различных источников: коллекций в памяти (массивы, списки), баз данных, XML, JSON и других.

Ключевые концепции:

  1. Единый синтаксис: Одинаковые операторы (Where, Select, OrderBy) работают с разными источниками данных.
  2. Два стиля написания:
    • Методный синтаксис (Method Syntax): Цепочка методов расширения.
      var result = collection.Where(x => x.Age > 18)
                         .OrderBy(x => x.Name)
                         .Select(x => x.Email);
    • Синтаксис запросов (Query Syntax): Напоминает SQL.
      var result = from item in collection
               where item.Age > 18
               orderby item.Name
               select item.Email;

Два основных режима выполнения:

Аспект LINQ to Objects LINQ to Entities (или др. провайдеры)
Источник данных Любая коллекция, реализующая IEnumerable<T>. Источник, реализующий IQueryable<T> (напр., DbSet в Entity Framework).
Место выполнения Локально, в памяти приложения. Удаленно (например, на сервере БД). Запрос транслируется в язык источника (SQL).
Механизм Лямбда-выражения компилируются в делегаты. Лямбда-выражения преобразуются в деревья выражений (Expression Trees) для анализа и трансляции.
Отложенное выполнение Да. Запрос выполняется только при итерации или вызове материализующих методов (ToList(), ToArray(), Count()). Да. Запрос строится и отправляется на сервер только при материализации.

Практический пример с Entity Framework:

// Эта часть выполняется как IQueryable - запрос уходит в БД
var adultsQuery = dbContext.Users
    .Where(u => u.Age >= 18) // Превращается в SQL WHERE clause
    .OrderBy(u => u.LastName); // Превращается в SQL ORDER BY

// Запрос выполняется в БД только здесь, возвращаются данные в память
List<User> adultList = adultsQuery.ToList();

// Дальнейшая работа - это LINQ to Objects в памяти
var firstNames = adultList.Select(u => u.FirstName).Distinct();

Основные операторы:

  • Фильтрация: Where
  • Проекция: Select, SelectMany
  • Сортировка: OrderBy, OrderByDescending, ThenBy
  • Группировка: GroupBy
  • Агрегация: Count, Sum, Average, Min, Max
  • Соединение: Join, GroupJoin
  • Кванторы: Any, All, Contains

Преимущества: повышение читаемости кода, безопасность типов на этапе компиляции, производительность (особенно с IQueryable), унификация подхода к данным.

Ответ 18+ 🔞

А, LINQ, ёпта! Ну это ж та самая штука в C#, которая позволяет с данными работать, как будто ты не код пишешь, а заклинания какие-то читаешь. Вместо того чтобы в циклах путаться и списки перебирать, ты просто говоришь языку: «Хочу вот это, отсюда, вот так». И он тебе делает. Красота, блядь.

Представь, у тебя куча данных отовсюду: из оперативки, из базы, из XML-файла, который ещё твой дед писал. Раньше под каждый источник свой ритуал с бубном нужен был. А теперь — один синтаксис на всех. Как универсальный пульт, только вместо телека — данные.

Как это выглядит? Да по-разному, на самом деле.

Можно писать, как будто ты SQL-запрос сочиняешь, только на русском (ну, почти). Это синтаксис запросов:

var результат = от чела в коллекции
                где чел.Возраст > 18
                упорядочить по челу.Имя
                выбрать челу.Почту;

Выглядит непривычно, но читается, как книга. А можно по-пацански, цепочкой методов — это методный синтаксис:

var результат = коллекция.Where(x => x.Возраст > 18)
                         .OrderBy(x => x.Имя)
                         .Select(x => x.Почта);

Одно и то же, просто второй способ чаще используют — он компактнее и, когда привыкнешь, даже интуитивнее.

А вот тут, внимание, самый важный подвох, где все новички обжигаются! Есть два принципиально разных способа, как эта хуйня работает под капотом.

  1. LINQ to Objects — это когда ты работаешь с тем, что уже в памяти болтается: List, Array, что угодно, что IEnumerable. Тут всё просто: лямбда x => x.Возраст > 18 превращается в обычный делегат (типа маленькую функцию), и когда ты решаешь получить результат, эта функция пробегает по всем элементам в памяти и фильтрует их. Всё происходит тут, на твоём компе.

  2. LINQ to Entities (или к другим провайдерам, типа SQL) — это уже магия посерьёзнее. Тут источник — это, например, DbSet из Entity Framework, который реализует IQueryable<T>. И когда ты пишешь ту же лямбду, она не компилируется в делегат, а превращается в дерево выражений (Expression Tree). Это такая структура данных, которая описывает твой запрос: «где, возраст, больше, 18». А потом этот провайдер (EF) берёт это дерево, анализирует его и переводит на SQL! То есть твой красивый C#-код превращается в SELECT * FROM Users WHERE Age > 18. И выполняется этот запрос уже на сервере базы данных, а не в твоей программе. Это, блядь, офигенно эффективно, потому что с базы придут уже отфильтрованные и отсортированные данные, а не вся таблица целиком.

Простой пример, чтобы не еб... чтобы не запутаться:

Допустим, у тебя Entity Framework и база пользователей.

// Всё, что до ToList() — это построение IQueryable-запроса. В БД НИЧЕГО НЕ УШЛО ЕЩЁ!
var запросКБазе = dbContext.Пользователи
    .Where(u => u.Возраст >= 18) // Станет частью SQL WHERE
    .OrderBy(u => u.Фамилия);    // Станет частью SQL ORDER BY

// ВОТ ТУТ, в этой строчке, запрос материализуется. Весь сконструированный SQL улетает на сервер БД.
List<Пользователь> списокВзрослых = запросКБазе.ToList();

// А вот это уже будет LINQ to Objects, потому что мы работаем с готовым списком в памяти.
var уникальныеИмена = списокВзрослых.Select(u => u.Имя).Distinct();

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

Основные операторы, без которых нихуя не получится:

  • Where — отфильтровать, оставить только тех, кто подходит под условие. Основа основ.
  • Select — преобразовать или выбрать конкретные поля. Из объекта «Пользователь» сделать, например, только его «Email».
  • OrderBy / OrderByDescending — отсортировать. Понятно.
  • GroupBy — сгруппировать. Всех Василис в одну кучку, всех Петров — в другую.
  • Count, Sum, Average — посчитать количество, сумму, среднее. Агрегация, называется.
  • Any, All — проверить, есть ли хоть один подходящий или все ли подходят.
  • Join — соединить две коллекции по какому-то общему ключу. Как JOIN в SQL.

И главная фишка — отложенное выполнение. Запрос не выполняется, пока ты явно не потребуешь результат через ToList(), ToArray(), Count() или не начнёшь перебирать в foreach. Это позволяет строить запрос по кусочкам, динамически, что очень удобно.

В общем, инструмент, блядь, мощнейший. Сначала мозг ломает, а потом жить без него не можешь. Читаемость кода взлетает до небес, да и производительность часто выигрывает, особенно когда работаешь с базой и не тащишь в память тонны лишних данных.