Ответ
Внедрение зависимостей (DI) в C# реализуется через три основных паттерна, которые определяют, как зависимость передается в потребительский класс:
-
Внедрение через конструктор (Constructor Injection)
- Самый предпочтительный и распространенный подход.
- Зависимости объявляются как
readonlyполя и передаются через параметры конструктора. -
Гарантирует, что объект будет создан в валидном состоянии (все зависимости предоставлены).
public class OrderProcessor { private readonly IOrderValidator _validator; private readonly IOrderRepository _repository; private readonly ILogger<OrderProcessor> _logger; // Зависимости явно требуются при создании public OrderProcessor( IOrderValidator validator, IOrderRepository repository, ILogger<OrderProcessor> logger) { _validator = validator ?? throw new ArgumentNullException(nameof(validator)); _repository = repository ?? throw new ArgumentNullException(nameof(repository)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public void Process(Order order) { _validator.Validate(order); _repository.Save(order); _logger.LogInformation("Order {OrderId} processed.", order.Id); } }
-
Внедрение через метод (Method Injection)
- Зависимость передается в качестве параметра конкретного метода, которому она нужна.
- Полезно, когда зависимость требуется только для одного действия и не должна храниться в состоянии класса.
public class ReportGenerator { // IReportFormatter не является частью состояния класса public string GenerateReport(ReportData data, IReportFormatter formatter) { return formatter.Format(data); } }
-
Внедрение через свойство (Property Injection / Setter Injection)
- Зависимость устанавливается через публичное свойство после создания объекта.
-
Используется реже, так как объект может какое-то время находиться в невалидном состоянии (свойство
null). Часто применяется в фреймворках (например, для внедрения в контроллеры MVC) или для опциональных зависимостей.public class NotificationService { // Опциональный логгер. Сервис может работать и без него. public ILogger<NotificationService> Logger { get; set; } public void Send(string message) { Logger?.LogDebug("Sending: {Message}", message); // ... логика отправки } }
Ключевые практики:
- Всегда предпочитайте Constructor Injection для обязательных зависимостей.
- Программируйте на уровне интерфейсов, а не конкретных реализаций.
- Регистрируйте зависимости в DI-контейнере (например, в
Startup.csилиProgram.csASP.NET Core). - Избегайте антипаттерна Service Locator, так как он скрывает зависимости и усложняет тестирование.