← Назад к вопросам

Чем отличается Select от SelectMany в LINQ? Приведите пример использования SelectMany.?

2.0 Middle🔥 172 комментариев
#Entity Framework и ORM#Основы C# и .NET

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Отличия Select от SelectMany в LINQ

Select и SelectMany — это методы проекции в LINQ, но они принципиально различаются по своей семантике и результатам работы.

Ключевые различия

АспектSelectSelectMany
Тип возвращаемого значенияВозвращает IEnumerable<TResult>, где каждый элемент соответствует одному элементу исходной коллекцииВозвращает IEnumerable<TResult>, "разворачивая" вложенные коллекции в плоскую последовательность
Структура выводаСохраняет структуру: одна входная последовательность → одна выходная последовательность той же длиныИзменяет структуру: преобразует вложенные коллекции в единую плоскую последовательность
ПрименениеПроекция каждого элемента в новую формуПроекция с последующей конкатенацией (выборка из коллекций внутри коллекций)

Select выполняет преобразование один-к-одному: для каждого элемента исходной последовательности создается ровно один элемент в результирующей последовательности.

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

Пример использования SelectMany

Рассмотрим практическую ситуацию: у нас есть список отделов компании, и каждый отдел содержит список сотрудников. Нам нужно получить плоский список всех сотрудников из всех отделов.

// Модели данных
class Department
{
    public string Name { get; set; }
    public List<Employee> Employees { get; set; }
}

class Employee
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// Исходные данные
var departments = new List<Department>
{
    new Department 
    { 
        Name = "IT", 
        Employees = new List<Employee>
        {
            new Employee { Name = "Алексей", Age = 30 },
            new Employee { Name = "Мария", Age = 25 }
        }
    },
    new Department 
    { 
        Name = "HR", 
        Employees = new List<Employee>
        {
            new Employee { Name = "Иван", Age = 28 },
            new Employee { Name = "Ольга", Age = 32 },
            new Employee { Name = "Дмитрий", Age = 29 }
        }
    }
};

Сравнение подходов

Использование Select (неправильно для этой задачи):

// Select вернет IEnumerable<List<Employee>> - список списков
var employeeLists = departments.Select(d => d.Employees);

foreach (var employeeList in employeeLists)
{
    // Здесь employeeList - это List<Employee>, а не отдельный сотрудник
    Console.WriteLine($"Отдел содержит {employeeList.Count} сотрудников");
    // Для доступа к сотрудникам нужен вложенный цикл
}

Использование SelectMany (правильное решение):

// SelectMany "разворачивает" вложенные коллекции в плоский список
var allEmployees = departments.SelectMany(d => d.Employees);

// Теперь мы можем работать с плоским списком сотрудников
foreach (var employee in allEmployees)
{
    Console.WriteLine($"{employee.Name}, {employee.Age} лет");
}
// Вывод:
// Алексей, 30 лет
// Мария, 25 лет
// Иван, 28 лет
// Ольга, 32 лет
// Дмитрий, 29 лет

Расширенный пример SelectMany с индексом

// Получить всех сотрудников с указанием отдела, в котором они работают
var employeesWithDepartment = departments
    .SelectMany(
        department => department.Employees,
        (department, employee) => new 
        { 
            DepartmentName = department.Name,
            EmployeeName = employee.Name,
            EmployeeAge = employee.Age
        }
    );

foreach (var item in employeesWithDepartment)
{
    Console.WriteLine($"{item.EmployeeName} работает в отделе {item.DepartmentName}");
}

Дополнительные сценарии использования SelectMany

  1. Работа с вложенными массивами:
var arrays = new[] 
{
    new[] { 1, 2, 3 },
    new[] { 4, 5 },
    new[] { 6, 7, 8, 9 }
};

var flattened = arrays.SelectMany(arr => arr);
// Результат: [1, 2, 3, 4, 5, 6, 7, 8, 9]
  1. Генерация декартова произведения:
var colors = new[] { "Красный", "Синий" };
var sizes = new[] { "Маленький", "Большой" };

var products = colors.SelectMany(
    color => sizes,
    (color, size) => $"{color} {size}"
);
// Результат: ["Красный Маленький", "Красный Большой", 
//             "Синий Маленький", "Синий Большой"]
  1. Обработка коллекций внутри объектов:
// Получение всех уникальных навыков из всех сотрудников
var allSkills = departments
    .SelectMany(d => d.Employees)
    .SelectMany(e => e.Skills) // Предположим, что у Employee есть List<string> Skills
    .Distinct()
    .ToList();

Важные особенности SelectMany

  • SelectMany не выполняет глубокое копирование объектов — он работает с ссылками на исходные объекты.
  • Метод поддерживает отложенное выполнение (deferred execution), как и большинство методов LINQ.
  • Существует перегрузка метода с селектором результата, который позволяет сохранить информацию о родительском элементе.

Производительность

SelectMany может быть более эффективным, чем комбинация Select + Concat или вложенных циклов, поскольку:

  • Оптимизирован для работы с последовательностями
  • Минимизирует выделение памяти для промежуточных коллекций
  • Поддерживает потоковую обработку данных

В заключение, основное правило выбора: используйте Select, когда вам нужно преобразовать каждый элемент в другой элемент, и SelectMany, когда вам нужно преобразовать каждый элемент в коллекцию, а затем объединить все эти коллекции в одну плоскую последовательность.