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

OOP I — Classes e Objetos

Por que "tudo vive dentro de uma classe" em C# — classes, objetos, construtores, propriedades e os modificadores de acesso que protegem os dados de um objeto.

1. Classe vs. objeto

Uma classe é a planta baixa; um objeto é a casa construída a partir dela. Você pode ter várias casas (objetos/instâncias) construídas a partir da mesma planta (classe), cada uma com seus próprios valores.

C# Cliente.cs
public class Cliente
{
    public int Id { get; set; }
    public string Nome { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
}

var cliente1 = new Cliente { Id = 1, Nome = "Ana" };
var cliente2 = new Cliente { Id = 2, Nome = "Bruno" };
// dois objetos independentes, a partir da mesma classe
Comparando com JavaScript

Isso lembra um objeto literal do JS ({'{'} id: 1, nome: "Ana" {'}'}) ou uma class do ES6 — a diferença é que em C# a forma do objeto (quais propriedades existem, de que tipo) é fixada pela classe e checada em tempo de compilação. Não dá para criar um Cliente com uma propriedade Idade que não foi declarada na classe.

2. Propriedades (properties) — o get/set por trás dos panos

{'{'} get; set; {'}'} é açúcar sintático para um par de métodos que leem e escrevem um campo interno. A forma expandida ajuda a entender o que realmente acontece:

C# propriedade auto-implementada vs. expandida
// forma curta (auto-implementada) — o mais comum
public string Nome { get; set; } = string.Empty;

// forma expandida — equivalente, com um campo privado por trás
private string _nome = string.Empty;
public string Nome
{
    get { return _nome; }
    set { _nome = value; }
}

// propriedade com validação — só a forma expandida permite isso
private decimal _saldo;
public decimal Saldo
{
    get => _saldo;
    set
    {
        if (value < 0)
            throw new ArgumentException("Saldo não pode ser negativo");
        _saldo = value;
    }
}
Dica

Use a forma expandida sempre que a propriedade precisar de uma regra (validação, cálculo, notificação de mudança). Para o caso comum — só guardar um valor — a forma auto-implementada é suficiente e mais legível.

3. Construtores

Um construtor é um método especial, com o mesmo nome da classe, que roda quando um objeto é criado com new. É o lugar certo para garantir que um objeto nasce em um estado válido.

C# construtor obrigando dados válidos
public class Cliente
{
    public string Nome { get; }
    public string Email { get; }

    public Cliente(string nome, string email)
    {
        if (string.IsNullOrWhiteSpace(nome))
            throw new ArgumentException("Nome é obrigatório", nameof(nome));

        Nome  = nome;
        Email = email;
    }
}

var cliente = new Cliente("Ana", "ana@x.com"); // só assim é possível criar um Cliente
// new Cliente() -- não compila: não existe construtor sem parâmetros
Atenção

Propriedades declaradas só com get (sem set) só podem ser atribuídas dentro do construtor — depois disso, são efetivamente somente leitura. Esse é o caminho mais simples para criar objetos imutáveis em C#.

4. Modificadores de acesso

ModificadorVisível para
publicQualquer código, de qualquer lugar
privateSó dentro da própria classe (padrão se omitido)
protectedA própria classe e classes que herdam dela (capítulo 6)
internalQualquer código no mesmo projeto/assembly
class ContaBancaria private decimal _saldo; ← só a própria classe acessa public void Depositar(decimal v) ← qualquer código pode chamar O saldo só muda através do método público — nunca diretamente de fora.

Fig. 1 — Encapsulamento: dados privados, expostos e controlados só através de métodos/propriedades públicos.

5. Encapsulamento na prática

C# protegendo o estado interno
public class ContaBancaria
{
    private decimal _saldo;

    public decimal Saldo => _saldo; // só leitura pública

    public void Depositar(decimal valor)
    {
        if (valor <= 0)
            throw new ArgumentException("Valor deve ser positivo");
        _saldo += valor;
    }

    public void Sacar(decimal valor)
    {
        if (valor > _saldo)
            throw new InvalidOperationException("Saldo insuficiente");
        _saldo -= valor;
    }
}

var conta = new ContaBancaria();
conta.Depositar(100);
// conta._saldo = 999999; -- não compila: _saldo é privado

Esse é o motivo de existir encapsulamento: sem ele, qualquer código poderia colocar _saldo num valor impossível, ignorando as regras de negócio. Com private + métodos públicos controlando o acesso, a única forma de mudar o saldo é através de regras que você mesmo garante.

6. Métodos estáticos vs. de instância

C# static — pertence à classe, não a um objeto
public class Calculadora
{
    public static decimal Somar(decimal a, decimal b) => a + b; // não precisa de instância
}

decimal resultado = Calculadora.Somar(2, 3); // chama direto na classe

// Console.WriteLine, que você já usou desde o capítulo 1, é exatamente isso:
// um método estático da classe Console.

7. Onde isso te leva

Com classes, objetos e encapsulamento cobertos, o próximo capítulo adiciona a segunda metade da orientação a objetos: como uma classe reaproveita e especializa o comportamento de outra (herança), e como código genérico pode trabalhar com vários tipos diferentes de forma uniforme (polimorfismo).

📌 Resumo do capítulo

  • Classe é a definição; objeto é a instância criada com new.
  • {'{'} get; set; {'}'} é açúcar sintático para um campo privado com métodos de leitura/escrita — expanda quando precisar de validação.
  • Construtores garantem que um objeto nasce em estado válido; propriedades só com get criam imutabilidade.
  • Encapsulamento (private + acesso controlado por métodos públicos) protege o estado interno de um objeto.
  • Métodos static pertencem à classe, não a uma instância específica.

✏️ Praticando

  1. Escreva a classe Produto (Nome, Preco, Estoque) com um construtor que valida Preco > 0.
  2. Adicione métodos AdicionarEstoque(int quantidade) e RemoverEstoque(int quantidade) que impedem estoque negativo, mantendo o campo de estoque privado.
  3. Escreva um método estático Produto.CriarComEstoqueInicial(string nome, decimal preco, int estoqueInicial) como uma forma alternativa de criação.