
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/catchpor 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).