Injeção de Dependência (Dependency Injection) em C#: guia completo

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

O que é Dependency Injection em C# e para que serve



No desenvolvimento moderno com C# e .NET, um dos princípios-chave de design é o Dependency Injection (DI) — injeção de dependências. Esse padrão permite criar código fracamente acoplado, testável e facilmente sustentável. Em vez de a classe criar por si mesma instâncias dos objetos de que necessita (acoplamento rígido), as dependências são injetadas externamente.



O objetivo principal do DI é implementar o princípio Inversion of Control (IoC), onde o controle da criação de objetos é transferido para um contêiner ou framework externo. Isso muda radicalmente a arquitetura da aplicação, tornando-a flexível e pronta para mudanças.



O problema do acoplamento rígido (Tight Coupling)



Vamos considerar um exemplo típico sem DI:



public class OrderService{    private readonly EmailSender _emailSender;

public OrderService() { // Dependência rígida — criamos uma instância de uma classe concreta _emailSender = new EmailSender(); }

public void ProcessOrder(Order order) { // Lógica de processamento do pedido _emailSender.Send("Pedido processado"); }}


Aqui, OrderService depende diretamente de EmailSender. Se quisermos substituir o envio de email por SMS ou logging, teremos que alterar o código de OrderService. Isso viola o Open/Closed Principle e torna o teste unitário praticamente impossível.



Principais formas de injeção de dependências em C#



Em C#, existem três formas principais de implementar DI. Cada uma tem seus cenários de uso e características específicas.



1. Injeção via construtor (Constructor Injection)



Esta é a forma mais popular e recomendada no .NET. As dependências são passadas através dos parâmetros do construtor e geralmente armazenadas em campos privados, acessíveis apenas para leitura (readonly).



public interface INotificationService{    void Send(string message);}

public class EmailService : INotificationService{ public void Send(string message) { Console.WriteLine($"Email enviado: {message}"); }}

public class OrderService{ private readonly INotificationService _notificationService;

// A dependência é injetada através do construtor public OrderService(INotificationService notificationService) { _notificationService = notificationService; }

public void ProcessOrder(Order order) { // Lógica... _notificationService.Send("Pedido processado"); }}


Vantagens: fica claro quais dependências a classe precisa; as dependências são imutáveis após a criação do objeto; fácil de testar, substituindo por objetos mock.



2. Injeção via propriedade (Property Injection)



A dependência é definida através de uma propriedade pública (setter). É usada com menos frequência, quando a dependência não é obrigatória para o funcionamento da classe.



public class OrderService{    public INotificationService NotificationService { get; set; }

public OrderService() { }

public void ProcessOrder(Order order) { if (NotificationService != null) { NotificationService.Send("Pedido processado"); } }}


Desvantagem: pode-se esquecer de definir a dependência, o que resultará em NullReferenceException durante a execução. Recomenda-se usar apenas para dependências opcionais.



3. Injeção via método (Method Injection)



A dependência é passada diretamente no método onde é usada. Isso é conveniente quando a dependência é necessária apenas para uma ação específica.



public class OrderService{    public void ProcessOrder(Order order, INotificationService notificationService)    {        // Lógica...        notificat                

Blogs

Book Recommendations