Módulo 1 · C# — Capítulo 07

Coleções e Generics

List, Dictionary e o "T" que aparece entre os sinais de menor/maior — generics explicados a partir do problema que eles resolvem.

1. O problema que generics resolvem

Antes de generics existirem (C# 1.0), uma coleção guardava object — qualquer coisa. Isso funcionava, mas exigia converter o tipo toda vez que você lia um item, e o compilador não impedia misturar tipos incompatíveis na mesma coleção. Generics resolvem isso: você declara o tipo uma vez, na criação da coleção, e o compilador garante consistência a partir daí.

C# sem generics (não faça isso)
ArrayList lista = new ArrayList();
lista.Add(1);
lista.Add("texto"); // compila! mistura tipos

int numero = (int)lista[0]; // cast manual obrigatório
C# com generics (o padrão de hoje)
List<int> lista = new List<int>();
lista.Add(1);
// lista.Add("texto"); -- não compila

int numero = lista[0]; // sem cast — já é int

2. List<T> — a coleção mais usada no dia a dia

C# operações comuns de List<T>
var clientes = new List<Cliente>();

clientes.Add(new Cliente { Nome = "Ana" });
clientes.Add(new Cliente { Nome = "Bruno" });

clientes.Remove(clientes[0]);
bool existe = clientes.Any(c => c.Nome == "Bruno");
Cliente? encontrado = clientes.FirstOrDefault(c => c.Nome == "Bruno");
int total = clientes.Count;

foreach (var cliente in clientes)
    Console.WriteLine(cliente.Nome);
Comparando com JavaScript

List<T> é o equivalente direto de um Array do JS, mas tipado — todo item precisa ser do mesmo tipo T. Métodos como .Any() e .FirstOrDefault() vêm do LINQ (próximo capítulo), o equivalente C# de .some() e .find() do JS.

3. Dictionary<TKey, TValue> — pares chave/valor

C# Dictionary — o equivalente de um Map do JS
var estoquePorProduto = new Dictionary<int, int>();

estoquePorProduto[101] = 50;   // adiciona ou substitui
estoquePorProduto[102] = 30;

if (estoquePorProduto.TryGetValue(101, out int quantidade))
    Console.WriteLine(quantidade); // 50

foreach (var par in estoquePorProduto)
    Console.WriteLine($"Produto {par.Key}: {par.Value} unidades");
Atenção

Acessar uma chave inexistente com estoquePorProduto[999] lança KeyNotFoundException. Use sempre TryGetValue quando não tiver certeza de que a chave existe — o mesmo princípio de TryParse do capítulo 2.

4. Outras coleções úteis

ColeçãoQuando usar
HashSet<T>Itens únicos, sem ordem garantida — checagem de existência muito rápida
Queue<T>Fila FIFO (primeiro a entrar, primeiro a sair)
Stack<T>Pilha LIFO (último a entrar, primeiro a sair)
IEnumerable<T>A interface mais genérica — "algo que pode ser percorrido"; usada em assinaturas de método

5. Escrevendo seu próprio tipo genérico

Generics não são exclusividade das coleções prontas — você pode escrever suas próprias classes e métodos genéricos:

C# uma classe genérica simples
public class Resultado<T>
{
    public bool Sucesso { get; }
    public T? Dado { get; }
    public string? Erro { get; }

    private Resultado(bool sucesso, T? dado, string? erro)
        => (Sucesso, Dado, Erro) = (sucesso, dado, erro);

    public static Resultado<T> Ok(T dado) => new(true, dado, null);
    public static Resultado<T> Falha(string erro) => new(false, default, erro);
}

Resultado<Cliente> ObterCliente(int id)
{
    var cliente = BuscarNoBanco(id);
    return cliente is not null
        ? Resultado<Cliente>.Ok(cliente)
        : Resultado<Cliente>.Falha("Cliente não encontrado");
}

Resultado<T> funciona com qualquer tipo — Resultado<Cliente>, Resultado<int>, Resultado<List<Produto>> — escrito uma única vez. Esse padrão (encapsular sucesso/falha num tipo, em vez de lançar exceção para todo erro esperado) é comum em código C# moderno e vai reaparecer no módulo de APIs.

6. Onde isso te leva

Coleções são a matéria-prima do próximo capítulo: LINQ, o conjunto de operadores que transforma, filtra e agrega coleções de forma declarativa — o "primo" mais poderoso dos métodos .map()/.filter()/.reduce() que você já usa em JavaScript.

📌 Resumo do capítulo

  • Generics (List<T>, Dictionary<TKey,TValue>) garantem consistência de tipo em tempo de compilação, sem cast manual.
  • Dictionary é o equivalente C# de um Map do JS — use TryGetValue para evitar exceção em chave inexistente.
  • HashSet, Queue e Stack cobrem casos específicos além de listas comuns.
  • Você pode escrever suas próprias classes/métodos genéricos com <T>.

✏️ Praticando

  1. Crie uma List<Produto> com pelo menos 5 itens e escreva métodos para adicionar, remover e buscar por nome.
  2. Crie um Dictionary<string, List<Produto>> agrupando produtos por categoria.
  3. Implemente a classe genérica Resultado<T> do exemplo e use-a num método que busca um produto por Id.