Полное руководство по языку программирования С# 14 и платформе .NET 10 / LINQ

Соединение коллекций

Начальный ⏱ 6 мин чтения 👁 2 🕓 обновлено 11.08.2026

Соединение в LINQ используется для объединения двух разнотипных наборов в один. Для соединения используется оператор join или метод Join(). Как правило, данная операция применяется к двум наборам, которые имеют один общий критерий.

Оператор join

Оператор join имеет следующий формальный синтаксис:


from объект1 in набор1
join объект2 in набор2 on объект2.свойство2 equals объект1.свойство1

После оператора join идет выборка объектов из второй коллекции. После оператора on указывается критерий соединения - свойство объекта из второй выборки, а после оператора equals - свойство объекта из первой выборки, которому должно быть равно свойство объекта из второй выборки. Если эти свойства равны, то оба объекта попадают в финальный результат.

Например, у нас есть два класса:


record class Person(string Name, string Company);
record class Company(string Title, string Language);

Класс Person представляет пользователя и хранит два свойства: Name (имя) и Company (компания пользователя). Класс Company представляет компанию и хранит два свойства: Title (название компании) и Language (основной язык программирования в компании)

Объекты обоих классов будет иметь один общий критерий - название компании. Соединим по этому критерию два набора этих классов:


Person[] people =
{
    new Person("Tom", "Microsoft"), new Person("Sam", "Google"),
    new Person("Bob", "JetBrains"), new Person("Mike", "Microsoft"),
};
Company[] companies =
{
    new Company("Microsoft", "C#"),
    new Company("Google", "Go"),
    new Company("Oracle", "Java")
};
var employees = from p in people
             join c in companies on p.Company equals c.Title
             select new { Name = p.Name, Company = c.Title, Language = c.Language };

foreach (var emp in employees)
    Console.WriteLine($"{emp.Name} - {emp.Company} ({emp.Language})");

record class Person(string Name, string Company);
record class Company(string Title, string Language);

С помощью выражения

join c in companies on p.Company equals c.Title

объект p из списка people (то есть объект Person) соединяется с объектом c из списка companies (то есть с объектом Company), если значение свойства p.Company совпадает со значением свойства c.Title. Результатом соединения будет объект анонимного типа, который будет содержать три свойства. В итоге мы получим следующий вывод:


Tom - Microsoft (C#)
Sam - Google (Go)
Mike - Microsoft (C#)

Обратите внимание, что в массиве people есть объект new Person("Bob", "JetBrains"), но в массиве компаний компании с именем "JetBrains" нет, соответственно он не попал с результат. Аналогично в списке people нет объектов Person, которые бы соотствовали компании new Company("Oracle", "Java").

Метод Join

В качестве альтернативы можно было бы использовать метод Join():


Join(IEnumerable<TInner> inner, 
    Func<TOuter,TKey> outerKeySelector, 
    Func<TInner,TKey> innerKeySelector, 
    Func<TOuter,TInner,TResult> resultSelector);

Метод Join() принимает четыре параметра:

  • второй список, который соединяем с текущим

  • делегат, который определяет свойство объекта из текущего списка, по которому идет соединение

  • делегат, который определяет свойство объекта из второго списка, по которому идет соединение

  • делегат, который определяет новый объект в результате соединения

Перепишим предыдущий пример с использованием метода Join:


Person[] people =
{
    new Person("Tom", "Microsoft"), new Person("Sam", "Google"),
    new Person("Bob", "JetBrains"), new Person("Mike", "Microsoft"),
};
Company[] companies =
{
    new Company("Microsoft", "C#"),
    new Company("Google", "Go"),
    new Company("Oracle", "Java")
};
var employees = people.Join(companies, // второй набор
             p => p.Company, // свойство-селектор объекта из первого набора
             c => c.Title, // свойство-селектор объекта из второго набора
             (p, c) => new { Name = p.Name, Company = c.Title, Language = c.Language }); // результат

foreach (var emp in employees)
    Console.WriteLine($"{emp.Name} - {emp.Company} ({emp.Language})");

record class Person(string Name, string Company);
record class Company(string Title, string Language);

GroupJoin

Метод GroupJoin() кроме соединения последовательностей также выполняет и группировку.


GroupJoin(IEnumerable<TInner> inner, 
        Func<TOuter,TKey> outerKeySelector, 
        Func<TInner,TKey> innerKeySelector, 
        Func<TOuter, IEnumerable<TInner>,TResult> resultSelector);

Метод GroupJoin() принимает четыре параметра:

  • второй список, который соединяем с текущим

  • делегат, который определяет свойство объекта из текущей коллекции, по которому идет соединение и по которому будет идти группировка

  • делегат, который определяет свойство объекта из второй коллекции, по которому идет соединение

  • делегат, который определяет новый объект в результате соединения. Этот делегат получает группу - объект текущей коллекции, по которому шла группировка, и набор объектов из второй коллекции, которые сооставляют группу

Например, возьмем выше определенные массивы people и companies и сгуппируем всех пользователей по компаниям:


Person[] people =
{
    new Person("Tom", "Microsoft"), new Person("Sam", "Google"),
    new Person("Bob", "JetBrains"), new Person("Mike", "Microsoft"),
};
Company[] companies =
{
    new Company("Microsoft", "C#"),
    new Company("Google", "Go"),
    new Company("Oracle", "Java")
};
var personnel = companies.GroupJoin(people, // второй набор
             c => c.Title, // свойство-селектор объекта из первого набора
             p => p.Company, // свойство-селектор объекта из второго набора
             (c, employees) => new   // результат
             {
                 Title = c.Title,
                 Employees = employees
            });

foreach (var company in personnel)
{
    Console.WriteLine(company.Title);
    foreach(var emp in company.Employees)
    {
        Console.WriteLine(emp.Name);
    }
    Console.WriteLine();
}

record class Person(string Name, string Company);
record class Company(string Title, string Language);

Результатом выполнения программы будет следующий вывод:

Microsoft
Tom
Mike

Google
Sam

Oracle

Метод GroupJoin, также как и метод Join, принимает все те же параметры. Только теперь в последний параметр - делегат передаются объект компании и набор пользователей этой компании.

Обратите внимание, что для компании "Oracle" в массиве people нет пользователей, хотя для нее также создается группа.

Аналогичного результата можно добитьс и с помощью оператора join:


var personnel = from c in companies
                join p in people on c.Title equals p.Company into g
                select new   // результат
                {
                    Title = c.Title,
                    Employees = g
                };

Метод Zip

Метод Zip() последовательно объединяет соответствующие элементы текущей последовательности со второй последовательностью, которая передается в метод в качестве параметра. То есть первый элемент из первой последовательности объединяется с первым элементом из второй последовательности, второй элемент из первой последовательности соединяется со вторым элементом из второй последовательности и так далее. Результатом метода является коллекция кортежей, где каждый кортеж хранит пару соответствующих элементов из обоих последовательностей:


var courses = new List<Course> { new Course("C#"), new Course("Java") };
var students = new List<Student> { new Student("Tom"), new Student("Bob") };

var enrollments = courses.Zip(students);

foreach (var enrollment in enrollments)
    Console.WriteLine($"{enrollment.First} - {enrollment.Second}");

record class Course(string Title);  // учебный курс
record class Student(string Name);  // студент

Здесь метод Zip объединяет соответствующие элементы из списков courses и students. В результате создается новая коллекция, которая хранит набор кортежей. Каждый кортеж в ней имеет два элемента. Первый элемент из свойства First представляет объект текущей коллекции (в данном случае объект Course), а второй элемент (в свойстве Second) хранит объект второй последовательности (в данном случае объект Student). Консольный вывод:

Course { Title = C# } - Student { Name = Tom }
Course { Title = Java } - Student { Name = Bob }

Проверь себя

📋 Тест по теме «Соединение коллекций» Тест по теме

Практические задания

Лёгкое Ручная проверка Заполните пропуск в коде (1)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

from объект_____ in набор1
join объект2 in набор2 on объект2.свойство2 equals объект1.свойство1
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (2)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

record class _____(string Name, string Company);
record class Company(string Title, string Language);
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (3)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

Person[] people =
{
    _____ Person("Tom", "Microsoft"), new Person("Sam", "Google"),
    new Person("Bob", "JetBrains"), new Person("Mike", "Microsoft"),
};
Company[] companies =
{
    new Company("Microsoft", "C#"),
    new Company("Google", "Go"),
    new Company("Oracle", "Java")
};
var employees = from p in people
             join c in companies on p.Company equals c.Title
             select new { Name = p.Name, Company = c.Title, Language = c.Language };

foreach (var emp in employees)
    Console.WriteLine($"{emp.Name} - {emp.Company} ({emp.Language})");

record class Person(string Name, string Company);
record class Company(string Title, string Language);
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (4)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

join c in companies on p.Company _____ c.Title
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (5)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

Tom - Microsoft (C#)
Sam - Google (Go)
_____ - Microsoft (C#)
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (1)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

from объект_____ in набор1
join объект2 in набор2 on объект2.свойство2 _____ объект1.свойство1
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (2)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

record class Person(string Name, string Company);
record class Company(string _____, string _____);
Войдите, чтобы отправить решение
Среднее Ручная проверка Расставьте строки кода в правильном порядке
+20 XP

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

Mike - Microsoft (C#)
Tom - Microsoft (C#)
Sam - Google (Go)
Войдите, чтобы отправить решение
Сложное Ручная проверка Объясните и модифицируйте пример из урока «Соединение коллекций»
+30 XP

Опишите своими словами, что делает приведённый пример кода из урока «Соединение коллекций», и предложите одно осмысленное изменение (например, другое входное значение или дополнительное условие). Приведите итоговый код.

from объект1 in набор1
join объект2 in набор2 on объект2.свойство2 equals объект1.свойство1
Войдите, чтобы отправить решение
Сложное Ручная проверка Найдите потенциальную проблему в коде урока «Соединение коллекций»
+30 XP

Изучите приведённый фрагмент кода и опишите, при каких входных данных или условиях он может сработать не так, как ожидается (граничный случай, отсутствие проверки и т.п.). Предложите исправленный вариант.

record class Person(string Name, string Company);
record class Company(string Title, string Language);
Войдите, чтобы отправить решение
Мини-проект Ручная проверка Мини-проект по теме «Соединение коллекций»
+50 XP

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

Войдите, чтобы отправить решение
Домашнее задание Ручная проверка Домашнее задание по теме «Соединение коллекций»
+25 XP

Напишите собственную небольшую программу, применяющую материал темы «Соединение коллекций». Используйте не менее одной конструкции, показанной в примерах урока. Прикрепите код решения и краткое описание того, что он делает.

Войдите, чтобы отправить решение

Обсуждение (0)

Войдите, чтобы оставить комментарий.