Start Debugging
2025-04-08 Atualizado 2025-04-12 csharp-14csharpdotnetdotnet-10 Edit on GitHub

Construtores e eventos parciais no C# 14

O C# 14 permite declarar construtores de instância e eventos como membros parciais, dividindo definições entre arquivos para uma geração de código mais limpa e melhor separação de responsabilidades.

O C# 14 introduz uma nova capacidade de declarar construtores de instância e eventos como membros partial. Isso significa que você pode dividir a definição de um construtor ou evento em duas partes de uma classe parcial, de forma similar ao que o C# já permite há muito tempo com métodos parciais e propriedades parciais. Uma parte de uma classe parcial pode declarar um construtor ou evento, e outra parte pode implementá-lo. Isso é especialmente útil em cenários como geração de código, onde um arquivo pode ser gerado automaticamente e outro mantido manualmente por um desenvolvedor.

Construtores parciais no C# 14

Os construtores parciais permitem dividir o construtor de uma classe em duas declarações dentro de uma classe partial. Uma declaração fornece apenas a assinatura (uma declaração definidora) e outra fornece o corpo de fato (uma declaração implementadora). O compilador as funde para que se comportem como um único construtor em tempo de execução.

Regras principais para construtores parciais:

A seguir há um exemplo de um construtor parcial em ação. Imagine uma classe dividida em dois arquivos: uma parte autogerada declara um construtor e outra parte o implementa:

// File: Car.AutoGenerated.cs (auto-generated partial class)
public partial class Car
{
    // Defining partial constructor (signature only, no body)
    public partial Car(string model);
}

// File: Car.cs (developer-maintained partial class)
public partial class Car
{
    // Implementing partial constructor (actual body)
    public partial Car(string model)
    {
        // (Optional) Can call another constructor or base constructor here
        // : base() or : this(...) would be placed after the parameter list if needed.
        
        if (string.IsNullOrEmpty(model))
            throw new ArgumentException("Model must be provided", nameof(model));
        Console.WriteLine($"Car model set to {model}");
    }
}

Neste exemplo, o construtor da classe Car está dividido: a primeira parte declara que Car(string model) existe, e a segunda parte fornece a implementação que valida o argumento e imprime uma mensagem. A declaração definidora não pode incluir código; é apenas a assinatura terminando em ;. A declaração implementadora tem o corpo e também poderia incluir uma chamada à classe base (se Car herdasse de outra classe) ou chamar outro construtor via this(). Em tempo de compilação, o compilador garante que haja exatamente uma de cada parte e então trata o construtor como um único construtor unificado.

Eventos parciais no C# 14

Os eventos parciais permitem uma divisão similar para eventos em uma classe parcial. Uma parte da classe define o evento (sem lógica de add/remove, igual a uma declaração de evento automática normal), e outra parte fornece os acessores add/remove (o código que executa quando assinantes anexam ou removem manipuladores de evento).

Regras principais para eventos parciais:

Veja um exemplo demonstrando um evento parcial. Um arquivo declara o evento e outro arquivo implementa a lógica personalizada de add/remove:

// File: Sensor.AutoGenerated.cs (auto-generated partial class)
public partial class Sensor
{
    // Defining partial event (no body, just declaration)
    public partial event EventHandler DataReceived;
}

// File: Sensor.cs (developer-maintained partial class)
public partial class Sensor
{
    // Implementing partial event (with custom add/remove logic)
    private EventHandler _dataReceivedHandlers;  // backing storage for event handlers

    public partial event EventHandler DataReceived
    {
        add 
        {
            Console.WriteLine("Subscriber added to DataReceived");
            _dataReceivedHandlers += value;
        }
        remove 
        {
            Console.WriteLine("Subscriber removed from DataReceived");
            _dataReceivedHandlers -= value;
        }
    }

    // Optional: a method to raise the event safely from this class
    protected void OnDataReceived(EventArgs e)
    {
        _dataReceivedHandlers?.Invoke(this, e);
    }
}

Neste exemplo, a classe Sensor tem um evento DataReceived dividido entre dois arquivos. A parte autogerada da classe declara que existe um evento DataReceived do tipo EventHandler. A parte do desenvolvedor fornece a implementação real: define um campo privado para armazenar os assinantes e implementa os blocos add e remove para registrar uma mensagem e atualizar esse campo privado. Quando outro código faz sensor.DataReceived += Handler;, ele executa a lógica personalizada de add. Da mesma forma, cancelar a assinatura aciona a lógica personalizada de remove. A classe pode usar _dataReceivedHandlers para disparar o evento (por exemplo, no método OnDataReceived). Sem a parte implementadora, a declaração definidora sozinha não compilaria, já que o evento não tem respaldo por si só; o C# exige que a parte implementadora complete a funcionalidade do evento.

Comparação com versões anteriores do C#

Antes do C# 14, você não podia declarar construtores ou eventos como parciais. No C# 13 e anteriores, a palavra-chave partial só era válida em classes, métodos e (a partir do C# 13) propriedades/indexadores. Se você tentasse marcar um construtor ou um evento como parcial, o compilador produzia um erro. Essa limitação significava que não havia como dividir a implementação de um construtor ou evento entre arquivos diferentes.

Os desenvolvedores frequentemente usavam soluções alternativas para obter flexibilidade similar. Por exemplo, considere um cenário em que o código autogerado precisa chamar alguma lógica personalizada durante a construção do objeto. Sem construtores parciais, um padrão comum era usar um método parcial dentro do construtor. A parte autogerada de uma classe podia chamar esse método, enquanto a implementação do método ficava em outro arquivo. Por exemplo, em código C# antigo você podia ver:

// Before C# 14: using a partial method to extend constructor logic
public partial class Car
{
    public Car()
    {
        InitializeComponents();       // auto-generated initialization
        OnConstructed();              // call a partial method hook
    }

    // This partial method can be implemented in another file to add custom behavior
    partial void OnConstructed();
}

No segundo arquivo, o desenvolvedor podia implementar o método parcial OnConstructed para executar código adicional após InitializeComponents(). Esse padrão permitia injetar código personalizado no momento da construção, mas era indireto e um tanto desajeitado. Para eventos, não havia uma solução alternativa fácil; era preciso herdar e sobrescrever a funcionalidade ou modificar o código gerado, já que não dava para dividir a lógica de add/remove de um evento entre arquivos.

Com os construtores e eventos parciais do C# 14, essas soluções alternativas não são mais necessárias. Você pode declarar diretamente um construtor ou evento como parcial e fornecer a implementação separadamente, deixando o código mais direto. A nova abordagem é mais clara e elimina a necessidade de métodos parciais fictícios ou outros truques.

Casos de uso e benefícios

Os principais beneficiários dos construtores e eventos parciais são cenários envolvendo geração de código e ferramentas. Muitos frameworks e ferramentas geram código C# (por exemplo, designers de UI, ORMs ou ferramentas de scaffolding de interfaces) e frequentemente marcam classes como parciais para que desenvolvedores possam estender as classes geradas sem editar o código autogerado. Com o C# 14:

Em todos esses casos, construtores e eventos parciais tornam o código mais fácil de manter. O código autogerado pode declarar o que precisa existir, e o código escrito à mão pode se limitar ao que o desenvolvedor realmente quer implementar. Também não há custo de desempenho em tempo de execução por usar membros parciais; a separação existe apenas em tempo de compilação. O programa resultante se comporta como se você tivesse escrito o construtor ou o evento completo em um único lugar.

Ao permitir que construtores de instância e eventos sejam partial, o C# 14 completa o conjunto de recursos de classes parciais para cobrir quase todos os tipos de membros. Esse aprimoramento melhora a capacidade da linguagem de oferecer suporte a uma separação limpa entre código autogerado e código personalizado. Os desenvolvedores ganham flexibilidade para organizar e estender o comportamento das classes sem gambiarras, e os source generators ganham uma nova ferramenta poderosa para injetar funcionalidade de forma modular. Quer você esteja trabalhando em frameworks grandes ou apenas dividindo código por clareza, construtores e eventos parciais oferecem uma abordagem mais expressiva e conveniente no C# 14.

Fontes

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Voltar