0Pricing
C# Academy · Aula

Injeção pelo construtor e interfaces

Aplique a injeção pelo construtor com um design baseado em interfaces para desacoplar componentes e permitir testes unitários.

Injeção pelo construtor e interfaces é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C# Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C# Academy inclui 4 aulas no total.

Fundamentos da injeção pelo construtor

A injeção pelo construtor é o padrão de DI mais comum. Uma classe declara suas dependências como parâmetros do construtor, e o contêiner as fornece no momento da instanciação.

Isso torna as dependências explícitas, obrigatórias e visíveis.

Definindo a interface

Comece com uma interface que defina o contrato. Isso permite trocar diferentes implementações (real, simulada ou em memória) sem alterar o código dependente.

public interface IProductRepository
{
    Task<Product?> GetByIdAsync(int id);
    Task<IEnumerable<Product>> GetAllAsync();
    Task AddAsync(Product product);
}

Implementando a interface

A implementação concreta executa o trabalho real — chamadas ao banco de dados, solicitações HTTP etc. O consumidor nunca precisa saber qual implementação recebe.

public class SqlProductRepository : IProductRepository
{
    private readonly AppDbContext _db;

    public SqlProductRepository(AppDbContext db) => _db = db;

    public async Task<Product?> GetByIdAsync(int id)
        => await _db.Products.FindAsync(id);

    public async Task<IEnumerable<Product>> GetAllAsync()
        => await _db.Products.ToListAsync();

    public async Task AddAsync(Product product)
    {
        _db.Products.Add(product);
        await _db.SaveChangesAsync();
    }
}

Consumindo por meio do construtor

A camada de serviço recebe IProductRepository como parâmetro do construtor. Ela nunca conhece SqlProductRepository — apenas o contrato da interface.

public class ProductService
{
    private readonly IProductRepository _repo;
    private readonly ILogger<ProductService> _logger;

    public ProductService(
        IProductRepository repo,
        ILogger<ProductService> logger)
    {
        _repo = repo;
        _logger = logger;
    }

    public async Task<Product?> GetProductAsync(int id)
    {
        _logger.LogInformation("Fetching product {Id}", id);
        return await _repo.GetByIdAsync(id);
    }
}

Registrando a cadeia

Registre todos os serviços da cadeia. O contêiner resolve automaticamente todo o grafo de dependências — ProductService → IProductRepository → AppDbContext.

builder.Services.AddDbContext<AppDbContext>(opt =>
    opt.UseSqlite("Data Source=app.db"));

builder.Services.AddScoped<IProductRepository, SqlProductRepository>();
builder.Services.AddScoped<ProductService>();

Testando com um objeto simulado

Como as dependências são injetadas por meio de interfaces, os testes unitários podem receber um objeto simulado ou falso sem acessar o banco de dados. Esse é um dos maiores benefícios da DI.

// Using NSubstitute or Moq in a unit test
var mockRepo = Substitute.For<IProductRepository>();
mockRepo.GetByIdAsync(1).Returns(new Product { Id = 1, Name = "Widget" });

var logger = Substitute.For<ILogger<ProductService>>();
var svc = new ProductService(mockRepo, logger);

var result = await svc.GetProductAsync(1);
Assert.Equal("Widget", result?.Name);

Vários parâmetros de construtor

Uma classe pode ter muitos parâmetros de construtor. O contêiner resolve todos, desde que cada um esteja registrado. Mantenha os construtores focados — parâmetros demais são um indício de que a classe tem responsabilidades demais.

public class OrderService
{
    public OrderService(
        IOrderRepository orders,
        IProductRepository products,
        IEmailSender email,
        ILogger<OrderService> logger)
    {
        // all injected by DI container
    }
}

Evitando o antipadrão do localizador de serviços

Não (NOT) injete IServiceProvider e chame GetService dentro de métodos de negócio. Isso oculta as dependências e dificulta os testes. Prefira a injeção pelo construtor para obter um código explícito e testável.

// BAD
public void Process(IServiceProvider sp)
{
    var repo = sp.GetService<IProductRepository>(); // hidden dep!
}

// GOOD
public class Processor
{
    private readonly IProductRepository _repo;
    public Processor(IProductRepository repo) => _repo = repo;
}

Sintaxe de construtor primary (C# 12)

O C# 12 introduz construtores primary para todos os tipos de classe, permitindo declarar parâmetros diretamente na declaração da classe para obter um código de DI mais conciso.

// C# 12 primary constructor
public class ProductService(
    IProductRepository repo,
    ILogger<ProductService> logger)
{
    public async Task<Product?> GetAsync(int id)
    {
        logger.LogInformation("Getting {Id}", id);
        return await repo.GetByIdAsync(id);
    }
}

Trocando implementações

Uma vantagem importante da DI baseada em interfaces é poder trocar uma implementação em uma única linha sem alterar nenhum código consumidor. Isso é útil para alternar entre bancos de dados, provedores de e-mail ou sinalizadores de funcionalidade.

// Switch from SQL to in-memory for integration tests
if (environment.IsEnvironment("Test"))
    builder.Services.AddScoped<IProductRepository, InMemoryProductRepository>();
else
    builder.Services.AddScoped<IProductRepository, SqlProductRepository>();

Na prática: controlador com DI

Os controladores do ASP.NET Core usam nativamente a injeção pelo construtor. A estrutura resolve todos os parâmetros e os fornece ao controlador.

[ApiController]
[Route("api/products")]
public class ProductsController : ControllerBase
{
    private readonly ProductService _svc;

    public ProductsController(ProductService svc) => _svc = svc;

    [HttpGet("{id}")]
    public async Task<IActionResult> Get(int id)
    {
        var product = await _svc.GetProductAsync(id);
        return product is null ? NotFound() : Ok(product);
    }
}

Verificação rápida

Qual é o principal benefício de programar para interfaces ao usar DI?

Recapitulação: injeção pelo construtor e interfaces

Principais conclusões:

  • Declare as dependências como parâmetros do construtor — o contêiner as fornece
  • Defina interfaces para os contratos dos serviços; injete a interface, não o tipo concreto
  • As interfaces permitem criar objetos simulados facilmente nos testes unitários e trocar implementações
  • Os construtores primary do C# 12 reduzem o código repetitivo em classes com muitas dependências injetadas
  • Evite o antipadrão do localizador de serviços — mantenha as dependências explícitas

Perguntas Frequentes

A aula “Injeção pelo construtor e interfaces” é grátis?

Sim — o texto completo de “Injeção pelo construtor e interfaces” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C# Academy, atualize para CoddyKit PRO. O curso de C# Academy inclui 4 aulas no total.

O que vou aprender em “Injeção pelo construtor e interfaces”?

Aplique a injeção pelo construtor com um design baseado em interfaces para desacoplar componentes e permitir testes unitários. Você pratica C# Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar C# Academy?

Nenhuma experiência prévia é necessária. C# Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Injeção pelo construtor e interfaces”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de C# Academy?

Sim. Cada aula de C# Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Fundamentos do contêiner de DI
  2. Ciclos de vida dos serviços: Transient, Scoped e Singleton
  3. Injeção pelo construtor e interfaces
  4. Padrões Factory e Options
← Voltar para C# Academy