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

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/GetHashCode automaticamente; 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/readonly onde 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).