Injeção de Dependência em C#: guia prático com exemplos

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

Introdução: o que é Dependency Injection e por que é necessário

Dependency Injection (DI) é um dos principais padrões de design que implementa o princípio de inversão de dependências (Dependency Inversion Principle) do SOLID. Na prática, isso significa que os objetos não criam suas próprias dependências, mas as recebem de fora. Isso torna o código flexível, testável e com baixo acoplamento.

No ecossistema C# e .NET, o DI tornou-se o padrão de facto. A partir do .NET Core, a plataforma possui seu próprio contêiner de injeção de dependência, usado no ASP.NET Core, serviços em segundo plano e até mesmo em aplicativos de console. Sem entender DI, é impossível imaginar o desenvolvimento moderno em C#.

Neste artigo, vamos analisar o DI com exemplos reais: desde a injeção manual até o uso do contêiner embutido do .NET. Você verá como eliminar acoplamentos rígidos e tornar o código adequado para testes de unidade.



O problema sem Dependency Injection

Vamos imaginar um cenário típico: a classe OrderService processa pedidos e envia notificações por e-mail. Sem DI, o código fica assim:

public class EmailSender{    public void Send(string to, string subject, string body)    {        Console.WriteLine($"Enviando e-mail para {to}: {subject}");    }}

public class OrderService{ private EmailSender _emailSender;

public OrderService() { // Acoplamento rígido — a classe cria sua própria dependência _emailSender = new EmailSender(); }

public void ProcessOrder(int orderId) { // Lógica de processamento do pedido... _emailSender.Send("user@example.com", "Pedido confirmado", $"Seu pedido #{orderId} foi recebido."); }}

Qual é o problema? OrderService está rigidamente acoplado à implementação específica de EmailSender. Se quisermos substituir o envio de e-mail por SMS ou registro em log, teremos que alterar o código de OrderService. O teste também se torna mais difícil: não é possível substituir o EmailSender real por um objeto mock.



Solução: injeção de dependências via construtor

O primeiro passo para o DI é extrair uma abstração (interface) e passar a dependência pelo construtor. Isso é chamado de Constructor Injection.



Passo 1: Criar a interface

public interface INotificationService{    void Send(string to, string subject, string body);}


Passo 2: Implementar a interface

public class EmailSender : INotificationService{    public void Send(string to, string subject, string body)    {        Console.WriteLine($"E-mail enviado: {subject} -> {to}");    }}

public class SmsSender : INotificationService{ public void Send(string to, string subject, string body) { Console.WriteLine($"SMS enviado: {subject} -> {to}"); }}


Passo 3: Injetar a dependência pelo construtor

public class OrderService{    private readonly INotificationService _notificationService;

// A dependência é passada de fora public OrderService(INotificationService notificationService) { _notificationService = notificationService; }

public void ProcessOrder(int orderId) { // Lógica de processamento do pedido... _notificationService.Send("user@example.com", "Pedido confirmado", $"Seu pedido #{orderId} foi recebido."); }}

Agora, OrderService não sabe qual implementação específica está sendo usada. Podemos usar EmailSender, SmsSender ou qualquer outra classe que implemente INotificationService. O código se tornou flexível e testável.



Usando o contêiner IoC embutido no .NET

Criar todas as dependências manualmente é inconveniente. O .NET possui um contêiner embutido (contêiner IoC) que gerencia automaticamente o ciclo de vida dos objetos. Vamos ver um exemplo para um aplicativo de console.



Passo 1: Instalar o pacote

Blogs

Book Recommendations