Capítulo 09: Classes Modernas, Construtores Primários e Encapsulamento
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...]
- SyncBlockIndex (8 bytes em 64-bit): Usado para travas de sincronização
lock(obj), suporte aGetHashCode()padrão e referências fracas. - 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).
- 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.