💻 Exemplos: Capítulo 12: Generics Avançados, Constraints Estritas e Variância

Especialização em Sistemas com C# • Demonstrações Práticas Compiláveis


🎯 Objetivo dos Exemplos

Demonstrar a metaprogramação segura com Generics em C#: restrições estritas de tipos (Generic Constraints como where T : unmanaged, class, new()), otimização em tempo de compilação sem boxing/unboxing e aplicação prática de Covariância (out) e Contravariância (in) em interfaces genéricas.


1. Código Fonte Completo (Program.cs)

/**
 * @file Program.cs
 * @brief Generics com constraints unmanaged/new, covariância e contravariância em C# 12.
 */

using System;
using System.Runtime.CompilerServices;

Console.WriteLine("=== CAPÍTULO 12: GENERICS AVANÇADOS E VARIÂNCIA ===");

// 1. Classe Genérica com Constraint 'unmanaged' (Sem ponteiros de GC / Blittable)
// Permite operações com tamanho em bytes exato via Unsafe.SizeOf
public class RingBufferNativo<T>(int capacidade) where T : unmanaged
{
    private readonly T[] _buffer = new T[capacidade];
    private int _indice;

    public void Gravar(T item)
    {
        _buffer[_indice % _buffer.Length] = item;
        _indice++;
    }

    public int TamanhoEmBytesTotal => _buffer.Length * Unsafe.SizeOf<T>();
    public int Capacidade => _buffer.Length;
}

// 2. Interface Covariante com 'out' (Permite retornar tipos mais derivados)
public interface IProdutor<out T>
{
    T Produzir();
}

// 3. Interface Contravariante com 'in' (Permite consumir tipos mais genéricos)
public interface IConsumidor<in T>
{
    void Consumir(T item);
}

// Hierarquia para Teste de Variância
public class Mensagem { public string Texto { get; set; } = ""; }
public class AlertaCritico : Mensagem { public int NivelUrgencia { get; set; } }

public class ProdutorAlertas : IProdutor<AlertaCritico>
{
    public AlertaCritico Produzir() => new() { Texto = "Falha no disco SAS", NivelUrgencia = 5 };
}

public class ConsumidorMensagens : IConsumidor<Mensagem>
{
    public void Consumir(Mensagem item) => Console.WriteLine($"[CONSUMIDOR] Mensagem: {item.Texto}");
}

// Execução 1: RingBuffer com Tipos Unmanaged
var bufferFloats = new RingBufferNativo<float>(100);
bufferFloats.Gravar(1.23f);
bufferFloats.Gravar(4.56f);

Console.WriteLine($"RingBuffer<float> -> Capacidade: {bufferFloats.Capacidade} | Tamanho Fisico: {bufferFloats.TamanhoEmBytesTotal} bytes");

var bufferInts = new RingBufferNativo<long>(50);
Console.WriteLine($"RingBuffer<long>  -> Capacidade: {bufferInts.Capacidade} | Tamanho Fisico: {bufferInts.TamanhoEmBytesTotal} bytes");

// Execução 2: Teste de Covariância (out)
// IProdutor<AlertaCritico> pode ser atribuído a IProdutor<Mensagem>!
IProdutor<Mensagem> produtorGenerico = new ProdutorAlertas();
Mensagem msg = produtorGenerico.Produzir();
Console.WriteLine($"\n[Covariância 'out'] Produzido: {msg.Texto} (Tipo real: {msg.GetType().Name})");

// Execução 3: Teste de Contravariância (in)
// IConsumidor<Mensagem> pode ser atribuído a IConsumidor<AlertaCritico>!
IConsumidor<AlertaCritico> consumidorEspecializado = new ConsumidorMensagens();
consumidorEspecializado.Consumir(new AlertaCritico { Texto = "Alta temperatura no rack 4", NivelUrgencia = 9 });
Console.WriteLine("[Contravariância 'in'] AlertaCritico consumido com sucesso por consumidor de Mensagem base.");

2. Instruções de Compilação e Execução

dotnet new console -n AppGenerics -f net8.0
cd AppGenerics
dotnet run

3. Saída Esperada no Terminal

=== CAPÍTULO 12: GENERICS AVANÇADOS E VARIÂNCIA ===
RingBuffer<float> -> Capacidade: 100 | Tamanho Fisico: 400 bytes
RingBuffer<long>  -> Capacidade: 50 | Tamanho Fisico: 400 bytes

[Covariância 'out'] Produzido: Falha no disco SAS (Tipo real: AlertaCritico)
[CONSUMIDOR] Mensagem: Alta temperatura no rack 4
[Contravariância 'in'] AlertaCritico consumido com sucesso por consumidor de Mensagem base.