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.
- 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 secretaria | Em C# | No exemplo |
|---|---|---|
| O modelo da ficha (em branco) | Classe | Aluno |
| Os campos da ficha | Campos e propriedades | Nome, Nota1, Nota2 |
| Uma ficha preenchida | Objeto (ou instância) | o aluno Bruno, a aluna Carla |
| "Imprimir uma ficha nova" | Criar um objeto com new | new Aluno() |
| O que a ficha "sabe fazer" (calcular a média do aluno) | Métodos | Media() |
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 opublic, 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
Nota1eNota2do 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ávelbruno. O tipo da variável é a própria classe:Alunovirou um tipo, comointestring. - bruno.Nome = "Bruno";
- O ponto quer dizer "de": "o nome do bruno recebe Bruno". Preenche um campo da ficha.
- bruno.Media()
- Pede ao objeto
brunoque calcule a média dele. A mesma chamada emcarlausa 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.
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.
| Coisa | Estado (campos e propriedades) | Comportamento (métodos) |
|---|---|---|
| Aluno | nome, notas, turma | calcular a média, dizer se foi aprovado |
| Conta bancária | titular, saldo | depositar, sacar, mostrar extrato |
| Produto da lojinha da Ana | nome, preço, estoque | aplicar desconto, vender uma unidade |
| Lâmpada | ligada ou desligada, potência | ligar, 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ção | Dados de um lado (variáveis, arrays), métodos do outro | Dados 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ípica | Media(notas1[i], notas2[i]) | aluno.Media() |
| Bom para | Programas pequenos, scripts, cálculos | Sistemas 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.
| Pilar | Em uma frase | Palavras do C# | Aula |
|---|---|---|---|
| Encapsulamento | Esconder os dados e expor ações seguras | private, public, get/set | Encapsulamento |
| Herança | Uma classe reaproveita outra e acrescenta | class B : A, base | Herança |
| Polimorfismo | Mesmo pedido, comportamento conforme o objeto | virtual, override | Polimorfismo |
| Abstração | Modelar só o essencial; contratos sem detalhes | abstract, interface | Classes 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:
| Termo | Significado | Também chamado |
|---|---|---|
| Classe | O molde, a descrição de um tipo de coisa | tipo (class) |
| Objeto | Uma coisa concreta criada a partir da classe | instância |
| Instanciar | Criar um objeto (new Aluno()) | criar uma instância |
| Campo | Variável declarada na classe, que cada objeto tem | atributo, variável de instância (field) |
| Propriedade | Um "campo com porteiro": parece campo por fora, mas passa por get/set | property |
| Método | Ação que o objeto sabe fazer | comportamento, operação |
| Membro | Qualquer coisa declarada dentro da classe (campo, propriedade, método…) | member |
| Estado | Os valores atuais dos campos de um objeto | |
| Referência | O "endereço" de um objeto, guardado numa variável | veja 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).
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";.
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.
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.
$ 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:
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 comnew 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. stringeRandomjá eram objetos. Os quatro pilares: encapsulamento, herança, polimorfismo e abstração.- Próxima aula: Classes e Objetos, linha por linha.