
C# para Iniciantes: Como Criar Tipos (classes, structs, interfaces, records, enums e genéricos)
Palavra-chave foco: criar tipos em C# (classes, structs, interfaces, records, enums, generics)
Aprender a criar tipos é o passo que transforma “scripts soltos” em código organizado, legível e escalável. Neste guia rápido, você vai entender quando usar cada tipo do C# e como aplicá-los em projetos reais.
O que são “tipos” no C#?
Tipos definem forma + comportamento dos seus dados. Eles permitem dar nomes claros às coisas do seu domínio (Cliente, Pedido, Produto), encapsular regras e reutilizar código.
Classes: o “padrão de fábrica” do C#
- Quando usar: objetos com estado mutável ou que participam de herança.
- Destaques: suporte total a herança, polimorfismo e encapsulamento.
public class Cliente
{
public string Nome { get; private set; }
public Cliente(string nome) => Nome = nome;
public void AtualizarNome(string novo) => Nome = novo;
}
✅ Use quando você precisa de um objeto “vivo”, que muda ao longo do tempo.
Structs: tipos de valor leves
- Quando usar: pequenos “pacotinhos” de dados imutáveis e baratos (ex.: coordenadas, medidas).
- Destaques: ficam na stack (em geral), evitando alocações no heap.
public readonly struct Coordenada(double x, double y)
{
public double X { get; } = x;
public double Y { get; } = y;
}
✅ Use quando o custo de alocação/importância de imutabilidade for prioridade.
Interfaces: contratos que liberam o polimorfismo
- Quando usar: definir o que um tipo faz sem amarrar como.
- Destaques: ideais para injeção de dependência e testes.
public interface INotificador
{
void Enviar(string mensagem);
}
public class EmailNotificador : INotificador
{
public void Enviar(string mensagem) { /* ... */ }
}
✅ Use quando precisar trocar implementações (email ↔ SMS) sem mudar o restante do código.
Records: dados imutáveis com valor semântico
- Quando usar: modelos de dados imutáveis, onde igualdade por valor faz sentido (DTOs, eventos, mensagens).
- Destaques: geram
Equals/GetHashCodeautomaticamente; sintaxe concisa.
public record Produto(string Id, string Nome, decimal Preco);
✅ Use quando quiser imutabilidade + igualdade por valor com mínimo de código.
Enums: valores nomeados e expressivos
- Quando usar: conjuntos fechados de opções (StatusDoPedido, PerfilDoUsuario).
- Destaques: aumentam clareza e evitam “strings mágicas”.
public enum StatusDoPedido { Novo, Pago, Enviado, Entregue, Cancelado }
✅ Use quando o domínio tiver um número finito de estados.
Genéricos: reutilize sem perder segurança de tipos
- Quando usar: algoritmos/estruturas que funcionam para vários tipos (listas, repositórios, serviços).
- Destaques: evitam
object/casts, dão performance e legibilidade.
public interface IRepositorio<T>
{
void Adicionar(T entidade);
T? ObterPorId(Guid id);
}
✅ Use quando quiser generalizar sem abrir mão de type safety.
Como escolher o tipo certo (checklist prático)
- Precisa de herança e métodos? → class
- Pacote pequeno de dados e imutável? → readonly struct
- Quer contrato para múltiplas implementações? → interface
- Modelo imutável com igualdade por valor? → record
- Conjunto pequeno de estados fixos? → enum
- API reutilizável para qualquer T? → genéricos
Boas práticas rápidas
- Imutabilidade por padrão: prefira
init/readonlyonde fizer sentido. - Encapsule invariantes: proteja o estado com
private set. - Interfaces finas: evite “interfaces gordas”; separe por responsabilidades.
- Nomeie pelo domínio:
Cliente,Pedido,Pagamento>Data1,ObjX. - Teste primeiro contratos: comece testando interfaces e records (são estáveis).
Exemplo integrado (mini-domínio)
public enum StatusPedido { Novo, Pago, Enviado }
public interface IPagamentos
{
bool Processar(decimal valor);
}
public record Item(string Sku, int Qtde, decimal PrecoUnit);
public class Pedido
{
private readonly List<Item> _itens = new();
public StatusPedido Status { get; private set; } = StatusPedido.Novo;
public void Adicionar(Item item) => _itens.Add(item);
public decimal Total() => _itens.Sum(i => i.Qtde * i.PrecoUnit);
public bool Pagar(IPagamentos servico)
{
if (Status != StatusPedido.Novo) return false;
if (servico.Processar(Total()))
{
Status = StatusPedido.Pago;
return true;
}
return false;
}
}
Por que isso é bom para iniciantes?
Você pratica classe (estado + regras), enum (estados), record (dados imutáveis), interface (contrato de pagamento) e já sente o ganho de coerência do domínio.
FAQ
Classes e records são a mesma coisa?
Não. Records priorizam dados imutáveis e igualdade por valor; classes são para objetos mutáveis e igualdade por referência (por padrão).
Quando usar struct e não class?
Em tipos pequenos e imutáveis onde performance/alocação importa (ex.: vetores, coordenadas, medidas). Evite structs “gordos”.
Posso misturar genéricos com interfaces?
Sim! É comum ter IRepositorio<T> e implementações específicas para cada agregado.
Post baseado no Capítulo 3 — “Creating Types in C#” do livro C# 12 in a Nutshell, de Joseph Albahari (O’Reilly).