Capítulo 11: Herança, Polimorfismo e Default Interface Methods

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


🗺️ Mapa Conceitual de Polimorfismo e Evolução de Interfaces

flowchart TD
    Abstracao["Abstração de Contratos"] --> ClassesAbstratas["Classes Abstratas (abstract class)"]
    Abstracao --> Interfaces["Interfaces (interface)"]

    subgraph CLASS_POLY["Polimorfismo Baseado em Classes"]
        AbstractMethod["abstract: Obriga Implementação na Subclasse"]
        VirtualMethod["virtual: Fornece Implementação Padrão Sobrescrevível"]
        Override["override: Especializa o Método na Derivada"]
        Sealed["sealed: Impede Especializações Subsequentes"]
    end

    subgraph INTERFACE_EVO["Evolução Moderna de Interfaces"]
        DIM["Default Interface Methods (DIM - C# 8+)"]
        StaticAbstract["Static Abstract Members in Interfaces (C# 11)"]
    end

    ClassesAbstratas --> CLASS_POLY
    Interfaces --> INTERFACE_EVO

    CLASS_POLY --> VTable["Despacho Dinâmico via VTable da CLR"]
    INTERFACE_EVO --> GenericMath["Aritmética Genérica e Contratos Estáticos"]

    style Abstracao fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
    style CLASS_POLY fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
    style INTERFACE_EVO fill:#e8f5e9,stroke:#4caf50,stroke-width:2px

🏛️ 1. Polimorfismo na CLR: Como Funciona o Despacho Virtual

No ecossistema .NET, os métodos são não-virtuais por padrão (ao contrário de Java, onde todos os métodos públicos são virtuais). Isso significa que, a menos que você marque explicitamente um método com a palavra-chave virtual, o compilador Roslyn emite a instrução IL call, que despacha a execução diretamente para o endereço de código fixo sem consultar tabelas de métodos virtuais.

Quando um método é marcado como virtual ou abstract:

  1. O compilador emite a instrução IL callvirt.
  2. O runtime consulta a MethodTable do objeto real no Heap gerenciado para resolver dinamicamente o método sobrescrito (override).
  3. Uma verificação implícita de nulidade (Null Check) é executada pela CPU antes do desvio.

⚙️ 2. Bloqueio de Herança e Otimizações com sealed

Marcar uma classe ou método como sealed impede que outras classes derivem dela ou continuem a sobrescrever aquele comportamento.

O Benefício Oculto de Performance do sealed:

Além de expressar a intenção de design de que a classe está completa, o uso de sealed permite ao RyuJIT realizar Devirtualização Estática:

  • Como o JIT sabe que não podem existir subclasses futuras daquela classe sealed, ele pode transformar chamadas virtuais caras em chamadas diretas normais.
  • O JIT pode até mesmo aplicar inlining no corpo do método, eliminando o custo de empilhar parâmetros na chamada de função!

📦 3. Default Interface Methods (DIM): Evolução Não Destrutiva de APIs

Historicamente, adicionar um novo método a uma interface pública publicada quebrava o código de todos os clientes que a implementavam, pois eles eram forçados a implementar o novo membro imediatamente.

Com os Default Interface Methods (introduzidos no C# 8):

  • Uma interface pode fornecer uma implementação padrão em seu próprio corpo.
  • Classes existentes continuam compilando e funcionando normalmente.
  • Classes que necessitarem de comportamento customizado podem optar por sobrescrever o método.
public interface IRepositório<T>
{
    Task<T> ObterPorIdAsync(int id);

    // Método com implementação padrão:
    async Task<bool> ExisteAsync(int id)
    {
        var item = await ObterPorIdAsync(id);
        return item != null;
    }
}

📊 4. Matriz Comparativa: Classes Abstratas vs Interfaces Modernas

Característica Classe Abstrata (abstract class) Interface Moderna (interface)
Herança Múltipla Não (apenas herança simples em C#) Sim (uma classe implementa N interfaces)
Estado de Instância (Campos) Sim (campos privados e propriedades com backing fields) Não (apenas propriedades sem estado)
Construtores de Instância Sim (inicializam a classe base) Não
Métodos com Implementação Sim (métodos concretos normais) Sim (via Default Interface Methods)
Membros Estáticos Abstratos Não Sim (static abstract para Generic Math)

🔬 5. Laboratório de Arquitetura: Membros Estáticos Abstratos em Interfaces (C# 11+)

Uma das adições mais sofisticadas do C# 11 é a capacidade de declarar membros static abstract em interfaces, viabilizando o projeto de Aritmética Genérica (Generic Math):

public interface IMonoid<T> where T : IMonoid<T>
{
    // Obriga o tipo a fornecer um valor neutro estático e operador de adição!
    static abstract T ElementoNeutro { get; }
    static abstract T operator +(T esquerda, T direita);
}

// Algoritmo genérico numérico sem saber o tipo concreto em tempo de design:
public static T SomarTudo<T>(ReadOnlySpan<T> itens) where T : IMonoid<T>
{
    T acumulador = T.ElementoNeutro;
    foreach (var item in itens)
    {
        acumulador += item;
    }
    return acumulador;
}

Isso permitiu ao .NET padronizar interfaces numéricas como INumber<T>, permitindo escrever algoritmos matemáticos universais que operam sobre int, double, float, decimal ou tipos numéricos customizados com performance nativa.