0
Promoção de Volta das Aulas ! Cursos com 25% OFF no menu "Cursos"
outubro 7, 2025
0
César Fontanella

C# para Iniciantes: Recursos Avançados Essenciais (delegates, eventos, lambdas, exceções, iteradores, nullable, pattern matching…)

Palavra-chave foco: recursos avançados do C# (delegates, eventos, lambdas, exceções, iteradores, nullable, pattern matching)

O Capítulo 4 apresenta os “superpoderes” da linguagem que tornam seu código mais expressivo, seguro e idiomático. Abaixo, um guia prático e direto para quem está começando e quer aplicar no dia a dia.


1) Delegates & Eventos: callbacks tipados

Quando usar: notificação/assinaturas (UI, domínio, integração) sem acoplamento forte.

public delegate void Notificacao(string msg);

public class Publicador
{
    public event Notificacao? Ocorreu;
    public void Disparar(string m) => Ocorreu?.Invoke(m);
}

var p = new Publicador();
p.Ocorreu += m => Console.WriteLine($"[evento] {m}");
p.Disparar("Pedido aprovado!");

Por que importa: eventos expõem um ponto de extensão seguro; delegates garantem assinatura tipada.


2) Lambdas, Func<> e Action<>: funções como dados

Quando usar: filtros, transformações, políticas configuráveis.

Func<int,int> dobrar = x => x * 2;
Action<string> log = s => Console.WriteLine(s);
log(dobrar(21).ToString());

Dica: lambdas permitem captura de variáveis, mas evite capturar estado mutável inadvertidamente.


3) Exceções: falhas controladas

Quando usar: condições excepcionais (não fluxo normal).

try
{
    Processar(pedido);
}
catch (ArgumentException ex) when (ex.ParamName == "quantidade")
{
    Console.WriteLine("Quantidade inválida.");
}
catch (Exception ex)
{
    Console.WriteLine($"Erro inesperado: {ex.Message}");
    throw; // preserva stack
}

Boas práticas: lançar tipos específicos, usar when para filtrar, não controlar fluxo com exceção.


4) Iteradores e yield: coleções sob demanda

Quando usar: geração/streaming de dados sem carregar tudo na memória.

public static IEnumerable<int> ParesAte(int n)
{
    for (int i = 0; i <= n; i++)
        if (i % 2 == 0) yield return i;
}

Benefício: lazy evaluation, ideal para pipelines LINQ.


5) Nullable Reference Types (NRT): adeus NullReferenceException

Ative nullable no projeto; então string? admite nulo, string não.

string? apelido = ObterApelido();
Console.WriteLine(apelido?.ToUpper()); // seguro

Use ?, ! (null-forgiving com parcimônia) e anotações corretas em APIs. Resultado: contratos mais claros.


6) Pattern Matching moderno

Quando usar: legibilidade para decisões complexas.

static decimal Frete(object pedido) => pedido switch
{
    { Peso: <= 1m, Expresso: false } => 9.90m,            // property pattern + relacional
    { Peso: <= 5m }                   => 19.90m,
    List<decimal> itens when itens.Count == 0 => 0m,      // list pattern + guard
    _ => 29.90m
};

Ganho: menos if aninhado, intenção explícita.


7) Sobrecarga de operadores (quando faz sentido)

Quando usar: tipos numéricos/geométricos/valores com semântica natural.

public readonly struct Moeda(decimal valor)
{
    public decimal Valor { get; } = valor;
    public static Moeda operator +(Moeda a, Moeda b) => new(a.Valor + b.Valor);
}

Evite “mágica”: só sobrecarregue quando o operador comunicar melhor seu domínio.


8) dynamic (use com cuidado)

Quando usar: interop dinâmico/COM/JSON “solto”, protótipos.

dynamic x = 10;
x = x + 5;      // resolvido em runtime

Custo: perde verificação em compile time; prefira tipos estáticos quando possível.


9) Generic Math (C# moderno)

Quando usar: algoritmos numéricos genéricos sem duplicar código.

static T Somar<T>(IEnumerable<T> nums) where T : System.Numerics.INumber<T>
    => nums.Aggregate(T.Zero, (acc, n) => acc + n);

Resultado: bibliotecas reutilizáveis e performáticas.


10) unsafe/fixo & diretivas de pré-processador

  • unsafe: apenas para cenários de alto desempenho/interop (ponteiros).
  • Pré-processador (#if, #nullable, #pragma): ajuste fino, diagnósticos e caminhos condicionais.

Boas práticas rápidas

  • Prefira lambdas puras (menos efeitos colaterais).
  • Documente com XML docs nos tipos e membros principais.
  • Valide argumentos e falhe cedo (ArgumentNullException.ThrowIfNull).
  • Trate exceções na borda (UI/infra), não espalhe try/catch por todo o domínio.
  • Teste patterns/iteradores com casos de borda (vazio, nulo, grande volume).

FAQ

Delegates x eventos: diferença?
Delegate é o tipo do callback; evento é o mecanismo que expõe/assina callbacks com segurança.

Quando usar dynamic?
Somente quando o tipo é realmente desconhecido em compile time (COM/interop, automação). Para JSON, prefira modelos tipados.

Nullable “resolve” nulls?
Ele previne nulls ao nível do compilador (análises/avisos). Você ainda precisa modelar corretamente ? e checagens.


Post baseado no Capítulo 4 — “Advanced C#” do livro C# 12 in a Nutshell, de Joseph Albahari (O’Reilly).