Injeção de Dependência em C#: um guia simples para iniciantes
Injeção de Dependência (DI) é um dos principais padrões de design no desenvolvimento moderno com C# e .NET. Se você está apenas começando sua jornada na programação, o termo pode parecer complexo, mas na verdade é muito mais simples. Neste artigo, vamos entender o que é Injeção de Dependência, para que serve e como usá-la na prática.
O que é Injeção de Dependência em palavras simples?
Imagine que você está montando um computador. Você pode comprar um gabinete pronto, onde tudo já está soldado e não pode ser substituído. Ou pode montá-lo a partir de componentes individuais: processador, placa de vídeo, memória RAM. A segunda abordagem é muito mais flexível — você pode facilmente trocar a placa de vídeo por uma mais potente ou adicionar mais um pente de memória.
A Injeção de Dependência funciona pelo mesmo princípio. Em vez de "soldar" rigidamente as dependências dentro da classe (criar objetos diretamente no código), você as "injeta" de fora. Isso torna seu código flexível, testável e fácil de manter.
Vamos considerar um exemplo ruim sem DI:
public class EmailService{ private SmtpClient _client; public EmailService() { // Acoplamento rígido — a classe cria a dependência sozinha _client = new SmtpClient("smtp.example.com", 587); } public void Send(string to, string message) { _client.Send(to, message); }}Agora o mesmo exemplo usando DI:
public class EmailService{ private readonly ISmtpClient _client; // A dependência é injetada através do construtor public EmailService(ISmtpClient client) { _client = client; } public void Send(string to, string message) { _client.Send(to, message); }}A diferença é clara: no segundo caso, trabalhamos através da interface ISmtpClient, e a implementação concreta é passada externamente.
Principais formas de injeção de dependências em C#
Em C#, existem três formas principais de implementar DI. Vamos analisar cada uma delas.
1. Injeção via construtor
Esta é a forma mais popular e recomendada. As dependências são passadas através dos parâmetros do construtor e armazenadas em campos privados.
public class OrderService{ private readonly IEmailService _emailService; private readonly ILogger _logger; public OrderService(IEmailService emailService, ILogger logger) { _emailService = emailService; _logger = logger; } public void ProcessOrder(Order order) { _logger.Log("Processamento do pedido iniciado"); _emailService.Send(order.CustomerEmail, "Seu pedido foi recebido"); _logger.Log("Processamento do pedido concluído"); }}2. Injeção via propriedade
A dependência é definida através de uma propriedade pública. Esta forma é útil quando a dependência é opcional.
public class ReportGenerator{ public ILogger Logger { get; set; } public void GenerateReport() { Logger?.Log("Gerando relatório..."); // Lógica de geração }}3. Injeção via método
A dependência é passada diretamente no método onde é necessária. Geralmente é usada com pouca frequência, mas pode ser útil em alguns cenários.
public class DataProcessor{ public void ProcessData(string data, IValidator validator) { if (validator.Validate(data)) { // Processamento de dados } }}Exemplo prático: do código ruim ao bom código
Vamos ver como o DI pode melhorar um código real. Suponha que temos uma classe para trabalhar com banco de dados.
Código ruim (sem DI):
public class UserRepository{ private SqlConnect