C# O que é POO

C# é uma linguagem orientada a objetos. Isso quer dizer que os programas são organizados em torno de "coisas" do problema (alunos, contas, produtos, pedidos), cada uma com os seus dados e as suas ações. Esta aula apresenta a ideia, sem pressa; as próximas ensinam cada peça em detalhe.

Você vai aprender
  • Que problema a programação orientada a objetos (POO) resolve.
  • A diferença entre classe (o molde) e objeto (cada coisa feita com o molde).
  • O que são estado (os dados de um objeto) e comportamento (o que ele sabe fazer).
  • Onde a classe fica num arquivo com instruções de nível superior: depois delas (senão, erro CS8803).
  • Que você já usa objetos desde o Nível 1 (string, Random).
  • A ideia dos quatro pilares: encapsulamento, herança, polimorfismo e abstração.

Todos os programas desta aula rodaram com dotnet run arquivo.cs na máquina fictícia da Ana (usuária ana, computador notebook), com o .NET 10 SDK, na pasta ~/cs.


O problema: dados espalhados

A Ana quer guardar, para cada aluno, o nome e duas notas, e calcular a média. Com o que você aprendeu até aqui (arrays e métodos), a solução natural é usar um array para cada informação:

string[] nomes = { "Bruno", "Carla", "Davi" };
double[] notas1 = { 8.0, 9.5, 5.0 };
double[] notas2 = { 7.0, 9.0, 6.0 };

for (int i = 0; i < nomes.Length; i++)
{
    double media = (notas1[i] + notas2[i]) / 2;
    Console.WriteLine($"{nomes[i]}: média {media}");
}
$ dotnet run alunos.cs
Bruno: média 7,5
Carla: média 9,25
Davi: média 5,5

Funciona. Mas repare como as informações de um aluno estão espalhadas: o nome do Bruno está em nomes[0], as notas dele em notas1[0] e notas2[0]. Nada no código diz que essas três posições formam "o Bruno"; é só um combinado de que o índice 0 de cada array é a mesma pessoa. Agora imagine:

  • A Ana acrescenta e-mail, telefone, turma e faltas: são sete arrays andando juntos.
  • Ela ordena os nomes em ordem alfabética com Array.Sort(nomes), mas esquece de reordenar as notas: o Bruno fica com as notas da Carla, sem nenhum erro na tela.
  • Ela quer um método MostrarAluno: precisa passar o índice e todos os arrays como parâmetros.

O que falta é um jeito de dizer ao C#: "um aluno é uma coisa só, que tem nome, nota 1 e nota 2, e sabe calcular a própria média". É exatamente isso que a orientação a objetos oferece.


A ideia: classe é o molde, objeto é o que sai dele

Pense na secretaria da escola da Ana. Existe uma ficha de matrícula em branco, impressa igual para todo mundo, com os campos "Nome", "Nota 1" e "Nota 2". Cada aluno que se matricula recebe uma ficha preenchida, com os seus dados.

Na secretariaEm C#No exemplo
O modelo da ficha (em branco)ClasseAluno
Os campos da fichaCampos e propriedadesNome, Nota1, Nota2
Uma ficha preenchidaObjeto (ou instância)o aluno Bruno, a aluna Carla
"Imprimir uma ficha nova"Criar um objeto com newnew Aluno()
O que a ficha "sabe fazer" (calcular a média do aluno)MétodosMedia()

Outra comparação muito usada: a forma de biscoito é a classe; cada biscoito é um objeto. A forma define o formato de todos, mas cada biscoito é um biscoito diferente, que pode ter cobertura diferente. E a forma não se come: você não usa a classe diretamente, usa os objetos criados com ela.

Em código, a classe Aluno e dois objetos dela ficam assim:

Aluno bruno = new Aluno();
bruno.Nome = "Bruno";
bruno.Nota1 = 8.0;
bruno.Nota2 = 7.0;

Aluno carla = new Aluno();
carla.Nome = "Carla";
carla.Nota1 = 9.5;
carla.Nota2 = 9.0;

Console.WriteLine($"{bruno.Nome}: média {bruno.Media()}");
Console.WriteLine($"{carla.Nome}: média {carla.Media()}");

class Aluno
{
    public string Nome = "";
    public double Nota1;
    public double Nota2;

    public double Media()
    {
        return (Nota1 + Nota2) / 2;
    }
}
$ dotnet run aluno.cs
Bruno: média 7,5
Carla: média 9,25

Você vai estudar cada linha nas próximas aulas. Por enquanto, uma leitura geral:

class Aluno { … }
Declara a classe Aluno: o molde. Ela fica no fim do arquivo, depois das instruções de nível superior (as linhas que o programa executa). Mais abaixo você vê o que acontece se ela vier antes.
public string Nome = "";
Um campo: uma variável que todo aluno tem. O public ("público") deixa o resto do programa ler e mudar esse campo; sem ele, o campo fica escondido dentro da classe. O = "" dá um texto inicial vazio. Os campos são o assunto de Campos, e o public, de public, private e internal.
public double Media() { … }
Um método do aluno. Diferente dos métodos que você escreveu em Métodos, ele usa Nota1 e Nota2 do próprio aluno, sem recebê-los como parâmetros.
Aluno bruno = new Aluno();
Cria um objeto novo da classe Aluno (uma ficha em branco) e o guarda na variável bruno. O tipo da variável é a própria classe: Aluno virou um tipo, como int e string.
bruno.Nome = "Bruno";
O ponto quer dizer "de": "o nome do bruno recebe Bruno". Preenche um campo da ficha.
bruno.Media()
Pede ao objeto bruno que calcule a média dele. A mesma chamada em carla usa as notas da Carla.

Agora tudo o que é do Bruno está junto, dentro de um objeto só. Não há índice para combinar, nem arrays para manter em ordem.

Maiúsculas: Nome, não nome

Em C#, o que é public numa classe (campos, métodos e, logo mais, propriedades) tem nome em PascalCase: começa com maiúscula, como Nome, Nota1 e Media. Variáveis locais e parâmetros continuam em camelCase: bruno, carla. É só convenção (o compilador aceitaria nome), mas todo código C# segue esse padrão; veja Nomes e Convenções.

Missão: crie um segundo aluno, davi, com notas 5.0 e 6.0, e mostre a média dele no mesmo formato: Davi: média 5,5. Siga o modelo do Bruno, e escreva as linhas novas antes da classe.

Aluno bruno = new Aluno();
bruno.Nome = "Bruno";
bruno.Nota1 = 8.0;
bruno.Nota2 = 7.0;

Console.WriteLine($"{bruno.Nome}: média {bruno.Media()}");

class Aluno
{
    public string Nome = "";
    public double Nota1;
    public double Nota2;

    public double Media()
    {
        return (Nota1 + Nota2) / 2;
    }
}
Aluno bruno = new Aluno();
bruno.Nome = "Bruno";
bruno.Nota1 = 8.0;
bruno.Nota2 = 7.0;

Aluno davi = new Aluno();
davi.Nome = "Davi";
davi.Nota1 = 5.0;
davi.Nota2 = 6.0;

Console.WriteLine($"{bruno.Nome}: média {bruno.Media()}");
Console.WriteLine($"{davi.Nome}: média {davi.Media()}");

class Aluno
{
    public string Nome = "";
    public double Nota1;
    public double Nota2;

    public double Media()
    {
        return (Nota1 + Nota2) / 2;
    }
}

A classe vai no fim do arquivo

Com dotnet run arquivo.cs, o arquivo começa direto pelas instruções que o programa executa, sem Main (veja Program.cs e o Main). As classes podem morar no mesmo arquivo, mas com uma regra: primeiro as instruções, depois as classes. Veja o que acontece ao contrário:

class Aluno
{
    public string Nome = "";
}

Aluno bia = new Aluno();
bia.Nome = "Bia";
Console.WriteLine(bia.Nome);
$ dotnet run antes.cs
/home/ana/cs/antes.cs(6,1): error CS8803: As instruções de nível superior precisam preceder as declarações de namespace e de tipo.

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

"Preceder" é "vir antes". A mensagem diz: as instruções de nível superior (a linha 6 em diante) precisam vir antes das declarações de tipo (a classe). Em inglês: Top-level statements must precede namespace and type declarations. Uma classe é um "tipo", e o compilador aponta a primeira instrução que veio depois dela.

Missão: rode e leia o erro CS8803. Depois conserte o programa sem apagar nada: só mude a classe de lugar, para que ele mostre Bia.

class Aluno
{
    public string Nome = "";
}

Aluno bia = new Aluno();
bia.Nome = "Bia";
Console.WriteLine(bia.Nome);
Aluno bia = new Aluno();
bia.Nome = "Bia";
Console.WriteLine(bia.Nome);

class Aluno
{
    public string Nome = "";
}

Em projetos maiores, cada classe costuma ir para o seu próprio arquivo (Aluno.cs). Você verá isso em Classes e Objetos.


Estado e comportamento

Todo objeto tem duas coisas:

  • Estado: o que ele é ou tem agora. São os valores dos campos. O estado do Bruno é "nome Bruno, nota 1 8,0, nota 2 7,0". Se a Ana corrige uma nota, o estado muda.
  • Comportamento: o que ele sabe fazer. São os métodos. Todo aluno sabe calcular a sua média.
CoisaEstado (campos e propriedades)Comportamento (métodos)
Alunonome, notas, turmacalcular a média, dizer se foi aprovado
Conta bancáriatitular, saldodepositar, sacar, mostrar extrato
Produto da lojinha da Ananome, preço, estoqueaplicar desconto, vender uma unidade
Lâmpadaligada ou desligada, potêncialigar, desligar

Uma dica para descobrir as classes de um problema: os substantivos (aluno, conta, produto) costumam virar classes; as características deles viram campos e propriedades; os verbos (depositar, vender) viram métodos.


Você já usa objetos

Desde o Nível 1, toda string é um objeto. A classe String foi escrita pelos criadores do .NET; cada texto que você cria é um objeto dela, com o seu próprio estado (as letras) e os métodos da classe:

string fruta = "banana";
string outra = "maçã";
Console.WriteLine(fruta.Length);
Console.WriteLine(outra.Length);
Console.WriteLine(fruta.ToUpper());
$ dotnet run objs.cs
6          « Length, aplicado ao estado "banana"
4          « o mesmo membro, outro objeto, outro resultado
BANANA

É a mesma estrutura do bruno.Media(): objeto.Membro. O Random de Números Aleatórios também: new Random() cria um objeto, e sorteio.Next(1, 7) chama um método dele. A diferença, a partir de agora, é que você vai escrever as suas próprias classes.


Programar "com procedimentos" × "com objetos"

Procedural (o que você fez até aqui)Orientado a objetos
OrganizaçãoDados de um lado (variáveis, arrays), métodos do outroDados e as ações sobre eles juntos, dentro de cada objeto
Pergunta que guia"Quais passos o programa executa?""Quais coisas existem e o que cada uma faz?"
Chamada típicaMedia(notas1[i], notas2[i])aluno.Media()
Bom paraProgramas pequenos, scripts, cálculosSistemas que crescem, com muitas "coisas" diferentes

Uma não substitui a outra: dentro dos métodos de um objeto você continua usando if, laços e variáveis do jeito que aprendeu. A POO é uma forma de organizar o programa em partes maiores.


Os quatro pilares (só a ideia)

Quatro ideias sustentam a orientação a objetos. Você não precisa dominá-las agora: cada uma tem a sua aula. O importante é saber que existem e reconhecer os nomes.

1. Encapsulamento: esconder os detalhes

O objeto protege os seus dados e só deixa mexer neles pelo que ele oferece. Pense no caixa eletrônico: você não abre o cofre e pega o dinheiro; você pede um saque, e a máquina confere o saldo antes. Em C#, isso se faz com private e propriedades (get e set). Aulas: Propriedades e Encapsulamento.

2. Herança: aproveitar uma classe para criar outra

Uma classe pode herdar tudo de outra e acrescentar o que é seu. Um AlunoBolsista é um Aluno com um campo a mais, o valor da bolsa; não é preciso reescrever nome, notas e média. Aula: Herança.

3. Polimorfismo: o mesmo pedido, respostas diferentes

"Poli" = muitas, "morfo" = formas. Objetos diferentes podem responder ao mesmo pedido cada um do seu jeito. Se a lojinha da Ana pede CalcularPreco() a um produto vendido por quilo e a um vendido por unidade, cada um calcula à sua maneira, e quem pediu não precisa saber qual é qual. Aula: Polimorfismo.

4. Abstração: ficar só com o que importa

Um modelo nunca copia a realidade inteira: ele escolhe o que é relevante para o programa. Para a secretaria, um aluno tem nome e notas; a cor do cabelo dele não interessa. A abstração também aparece como "contratos" que dizem o que fazer sem dizer como. Aulas: Classes Abstratas e Interfaces.

PilarEm uma frasePalavras do C#Aula
EncapsulamentoEsconder os dados e expor ações segurasprivate, public, get/setEncapsulamento
HerançaUma classe reaproveita outra e acrescentaclass B : A, baseHerança
PolimorfismoMesmo pedido, comportamento conforme o objetovirtual, overridePolimorfismo
AbstraçãoModelar só o essencial; contratos sem detalhesabstract, interfaceClasses Abstratas

Vocabulário

Você vai ver estes termos em livros, vagas de emprego e na documentação da Microsoft. Vários são sinônimos:

TermoSignificadoTambém chamado
ClasseO molde, a descrição de um tipo de coisatipo (class)
ObjetoUma coisa concreta criada a partir da classeinstância
InstanciarCriar um objeto (new Aluno())criar uma instância
CampoVariável declarada na classe, que cada objeto tematributo, variável de instância (field)
PropriedadeUm "campo com porteiro": parece campo por fora, mas passa por get/setproperty
MétodoAção que o objeto sabe fazercomportamento, operação
MembroQualquer coisa declarada dentro da classe (campo, propriedade, método…)member
EstadoOs valores atuais dos campos de um objeto
ReferênciaO "endereço" de um objeto, guardado numa variávelveja Classes e Objetos

Quando dá errado

Os primeiros erros com objetos quase sempre vêm de confundir a classe (o molde) com um objeto (uma ficha preenchida).

CS0120: usar a classe no lugar do objeto
Aluno.Nome = "Bruno";
Console.WriteLine(Aluno.Media());

class Aluno
{
    public string Nome = "";
    public double Nota1;
    public double Nota2;

    public double Media()
    {
        return (Nota1 + Nota2) / 2;
    }
}
$ dotnet run e1.cs
/home/ana/cs/e1.cs(1,1): error CS0120: Uma referência de objeto é necessária para o campo, o método ou a propriedade "Aluno.Nome" não estática
/home/ana/cs/e1.cs(2,19): error CS0120: Uma referência de objeto é necessária para o campo, o método ou a propriedade "Aluno.Media()" não estática
/home/ana/cs/e1.cs(7,19): warning CS0649: Campo "Aluno.Nota1" nunca é atribuído e sempre terá seu valor padrão 0
/home/ana/cs/e1.cs(8,19): warning CS0649: Campo "Aluno.Nota2" nunca é atribuído e sempre terá seu valor padrão 0

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

O que aconteceu: Aluno.Nome é como escrever o nome na ficha em branco, o modelo. O C# pergunta: o nome de qual aluno? "Uma referência de objeto é necessária": falta dizer qual objeto. Campos e métodos sem static ("não estáticos") pertencem a cada objeto, não à classe (o static tem aula própria: static). Os avisos CS0649 dizem que ninguém nunca preencheu as notas, o que é verdade.

Como resolver: crie um objeto e use a variável dele: Aluno bruno = new Aluno(); bruno.Nome = "Bruno";.

CS1955: esquecer o new
$ dotnet run e2.cs
/home/ana/cs/e2.cs(1,15): error CS1955: O membro não invocável "Aluno" não pode ser usado como um método.

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

O que aconteceu: a linha 1 era Aluno bruno = Aluno();. Sem o new, Aluno() parece a chamada de um método chamado Aluno. Mas Aluno é uma classe, e uma classe não se "chama" ("não invocável").

Como resolver: Aluno bruno = new Aluno();. O new é o que cria o objeto.

CS0122: esquecer o public
Aluno bruno = new Aluno();
bruno.Nome = "Bruno";
Console.WriteLine(bruno.Nome);

class Aluno
{
    string Nome = "";
}
$ dotnet run e3.cs
/home/ana/cs/e3.cs(2,7): error CS0122: "Aluno.Nome" é inacessível devido ao seu nível de proteção
/home/ana/cs/e3.cs(3,25): error CS0122: "Aluno.Nome" é inacessível devido ao seu nível de proteção
/home/ana/cs/e3.cs(7,12): warning CS0414: O campo "Aluno.Nome" é atribuído, mas seu valor nunca é usado

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

O que aconteceu: em C#, tudo o que está dentro de uma classe é privado por padrão: só a própria classe enxerga. Sem public, o Nome fica escondido, e o resto do programa não pode usá-lo ("inacessível devido ao seu nível de proteção"). Quem vem do Java estranha: lá, o padrão é mais aberto.

Como resolver: public string Nome = "";. A aula public, private e internal explica quando esconder de propósito.

CS8803: a classe veio antes das instruções
$ dotnet run antes.cs
/home/ana/cs/antes.cs(6,1): error CS8803: As instruções de nível superior precisam preceder as declarações de namespace e de tipo.

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

O que aconteceu: no arquivo, a class Aluno estava em cima e as instruções, embaixo.

Como resolver: mova as classes para o fim do arquivo (ou para arquivos próprios, num projeto).


Missão final: a lojinha da Ana

Missão: a classe Produto já está pronta, com os campos Nome e Preco e o método Mostrar(). Nas instruções do topo, crie dois objetos: uma maçã de 7.90m e uma banana de 3.50m (o m marca um decimal, o tipo do dinheiro), e chame Mostrar() em cada um.

Console.WriteLine("Lojinha da Ana");
// crie a maçã e a banana aqui

class Produto
{
    public string Nome = "";
    public decimal Preco;

    public void Mostrar()
    {
        Console.WriteLine($"{Nome}: {Preco:C}");
    }
}
Console.WriteLine("Lojinha da Ana");
Produto maca = new Produto();
maca.Nome = "maçã";
maca.Preco = 7.90m;

Produto banana = new Produto();
banana.Nome = "banana";
banana.Preco = 3.50m;

maca.Mostrar();
banana.Mostrar();

class Produto
{
    public string Nome = "";
    public decimal Preco;

    public void Mostrar()
    {
        Console.WriteLine($"{Nome}: {Preco:C}");
    }
}

Exercícios

1. Molde ou coisa?

Como se chama o "molde" que descreve como são todos os alunos?

2. Criar um objeto

Complete para criar um objeto da classe Produto:

Produto p =

3. O erro da ordem

Qual é o código do erro que aparece quando a classe vem antes das instruções de nível superior?

4. Na frase "a Ana tem 3 contas bancárias, e cada uma tem titular e saldo", o que vira classe?

5. O que é o estado de um objeto?

6. Por que bruno.Nome dá CS0122 quando o campo é declarado como string Nome = "";?


Resumo

  • A POO junta dados e ações numa coisa só, em vez de espalhar arrays paralelos.
  • Classe é o molde (class Aluno { … }); objeto é cada coisa criada com new Aluno().
  • Todo objeto tem estado (campos e propriedades) e comportamento (métodos). Use o ponto: bruno.Nome, bruno.Media().
  • Com instruções de nível superior, as classes vão no fim do arquivo; antes, dá CS8803.
  • O que é usado de fora precisa de public (senão, CS0122) e tem nome em PascalCase.
  • string e Random já eram objetos. Os quatro pilares: encapsulamento, herança, polimorfismo e abstração.
  • Próxima aula: Classes e Objetos, linha por linha.