Что такое IoC-контейнер?

«Что такое IoC-контейнер?» — вопрос из категории Архитектура, который задают на 32% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

IoC-контейнер (Inversion of Control, контейнер инверсии управления) — это библиотека или фреймворк, который автоматизирует внедрение зависимостей (Dependency Injection, DI). Вместо того чтобы код сам создавал свои зависимости (через new), контейнер берёт на себя ответственность за их создание, сборку и управление жизненным циклом.

Основные обязанности контейнера:

  1. Регистрация: Сопоставление абстракций (интерфейсов, абстрактных классов) с конкретными реализациями.
  2. Разрешение: Автоматическое создание объекта, рекурсивно создавая и внедряя все его зависимости.
  3. Управление жизненным циклом: Контроль за тем, как долго живёт экземпляр сервиса (Singleton, Scoped, Transient).

Практический пример с Microsoft.Extensions.DependencyInjection (стандарт для .NET):

// 1. Настройка контейнера (обычно в Program.cs или Startup.cs)
var services = new ServiceCollection();

// Регистрация сервисов с разным временем жизни
services.AddTransient<IOrderService, OrderService>(); // Новый экземпляр на каждый запрос
services.AddScoped<ICartRepository, CartRepository>(); // Один экземпляр на область (например, HTTP-запрос)
services.AddSingleton<ILogger, FileLogger>(); // Один экземпляр на всё приложение

// 2. Построение провайдера (контейнера)
IServiceProvider serviceProvider = services.BuildServiceProvider();

// 3. Разрешение зависимости
using (var scope = serviceProvider.CreateScope())
{
    // Для Scoped-сервисов необходимо создать область (scope)
    var orderService = scope.ServiceProvider.GetRequiredService<IOrderService>();
    orderService.ProcessOrder();
}

Популярные контейнеры для .NET: Autofac, Ninject, Simple Injector, Unity. Они часто предоставляют расширенные функции по сравнению со стандартным контейнером.

Преимущества использования:

  • Слабая связанность: Классы зависят от абстракций, а не от конкретных реализаций.
  • Упрощение тестирования: Зависимости легко подменить mock-объектами в unit-тестах.
  • Централизованное управление: Вся конфигурация зависимостей находится в одном месте ("композиционном корне").
  • Управление жизненным циклом: Контейнер заботится о правильном создании и освобождении ресурсов.