Capítulo 09: Classes Modernas, Construtores Primários e Encapsulamento

Especialização em Sistemas com C# • C# 12 & .NET 8 • POO Moderna, LINQ, Concorrência e Arquitetura Corporativa


🗺️ Mapa Conceitual da Estrutura de Classes e Encapsulamento em C# 12

flowchart TD
    Classe["Declaração de Classe Moderna"] --> Construtor["Primary Constructor: class Motor(string id, int rpm)"]
    Classe --> Propriedades["Propriedades com Acessores Estritos"]
    Classe --> Encapsulamento["Modificadores de Acesso"]

    subgraph PROPS["Acessores Modernos"]
        Init["init (Imutabilidade Pós-Construção)"]
        Required["required (Obrigatoriedade em Tempo de Compilação)"]
        PrivateSet["private / internal set (Proteção de Mutação)"]
    end

    subgraph ACCESS_MODIFIERS["Visibilidade e Encapsulamento"]
        Public["public (Acesso Total)"]
        Internal["internal (Visível Apenas no Mesmo Assembly)"]
        PrivateProtected["private protected (Subclasses no Mesmo Assembly)"]
        File["file (Escopo Restrito ao Arquivo Fonte - C# 11)"]
    end

    Propriedades --> PROPS
    Encapsulamento --> ACCESS_MODIFIERS

    style Classe fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
    style PROPS fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
    style ACCESS_MODIFIERS fill:#fff3e0,stroke:#ff9800,stroke-width:2px

🏛️ 1. O Design Orientado a Objetos Moderno em C# 12

O design de classes em C# evoluiu substancialmente. Padrões tradicionais repletos de cerimônia sintática (campos privados repetitivos _campo, construtores atribuindo parâmetro a parâmetro e getters/setters redundantes) foram substituídos por recursos idiomáticos concisos que eliminam código boilerplate sem sacrificar o rigor do encapsulamento.


⚙️ 2. Construtores Primários em Classes (Primary Constructors)

Introduzidos para Records no C# 9 e estendidos para todas as classes e structs normais no C# 12, os construtores primários colocam os parâmetros de inicialização diretamente na assinatura da classe:

// C# 12: Construtor Primário Elegante e Direto
public class ServicoTelemetria(string endpoint, int timeoutMs)
{
    // Os parâmetros 'endpoint' e 'timeoutMs' estão no escopo de toda a classe!
    private readonly string _endpoint = !string.IsNullOrWhiteSpace(endpoint) 
        ? endpoint 
        : throw new ArgumentNullException(nameof(endpoint));

    public void Conectar()
    {
        Console.WriteLine($"Conectando a {_endpoint} com timeout de {timeoutMs} ms.");
    }
}

Diferente de Records (onde o construtor primário gera automaticamente propriedades públicas), em classes comuns os parâmetros atuam como parâmetros de captura, permitindo ao desenvolvedor decidir livremente como inicializar campos privados e validar dados.


📦 3. As Propriedades init e o Modificador required

Para criar objetos com inicializadores de objeto limpos sem abrir mão da imutabilidade, o C# combinou dois recursos revolucionários:

3.1 Acessor init:

Permite que o valor de uma propriedade seja atribuído exclusivamente durante a fase de instanciação do objeto. Após a construção ser concluída, a propriedade torna-se estritamente imutável:

public class ConfiguracaoDb
{
    public string ConnectionString { get; init; } = "";
}

var cfg = new ConfiguracaoDb { ConnectionString = "Server=localhost;" };
// cfg.ConnectionString = "Outro"; // ERRO DE COMPILAÇÃO!

3.2 Modificador required (C# 11+):

Garante que quem instanciar o objeto seja obrigado pelo compilador a inicializar aquela propriedade através de inicializadores de objeto, eliminando construtores monstruosos com dezenas de sobrecargas opcionais:

public class RegistroAuditoria
{
    public required string UsuarioId { get; init; }
    public required string Acao { get; init; }
    public DateTime Timestamp { get; init; } = DateTime.UtcNow;
}

📊 4. Matriz Comparativa: Modificadores de Acesso no C# Moderno

Modificador Visível no Mesmo Assembly? Visível em Outro Assembly? Escopo Específico
public Sim Sim Acesso irrestrito
internal Sim Não Padrão corporativo para isolamento de biblioteca
protected Sim (apenas subclasses) Sim (apenas subclasses) Herança tradicional
protected internal Sim (qualquer classe) Sim (apenas subclasses) União de escopos
private protected Sim (apenas subclasses) Não Interseção estrita de escopo
file (C# 11+) Não (apenas no arquivo) Não Isolamento hermético para Source Generators

🔬 5. Laboratório de Arquitetura: Anatomia Física de uma Classe no Heap

Quando a instrução new MinhaClasse() é executada na CLR, o runtime aloca no Heap gerenciado um bloco contíguo de bytes composto por três seções:

[SyncBlockIndex: 8B] [MethodTablePtr: 8B] [Campos Membros da Instância...]
  1. SyncBlockIndex (8 bytes em 64-bit): Usado para travas de sincronização lock(obj), suporte a GetHashCode() padrão e referências fracas.
  2. MethodTable Pointer (8 bytes em 64-bit): Ponteiro direto para a estrutura interna da classe que contém os ponteiros de métodos, metadados e a tabela de despacho virtual (VTable da CLR).
  3. Membros de Dados: Posicionados sequencialmente de acordo com regras de alinhamento natural de 8 bytes da CPU.

Essa sobrecarga mínima de 16 bytes por objeto explica por que instanciar milhões de classes minúsculas com apenas um int é ineficiente comparado ao uso de structs ou records de valor.