C# do while
O while pergunta antes de cada volta. Se a resposta já começa "não", o corpo nunca roda. Mas há situações em que você precisa fazer a coisa pelo menos uma vez para depois decidir se repete: mostrar o menu antes de saber se a pessoa quer sair, pedir a senha antes de saber se ela está certa, jogar uma rodada antes de perguntar "jogar de novo?". Para isso existe o do … while ("faça … enquanto"): primeiro faz, depois testa.
- Como escrever
do { … } while (condição);e em que ordem ele roda. - A diferença para o
while: odo whileroda pelo menos uma vez. - Os dois usos clássicos: pedir de novo até a entrada ser válida e o menu que repete.
- O
;obrigatório no fim (CS1002) e a variável do corpo que "some" na condição (CS0103).
Todos os programas desta aula rodaram com o .NET 10 (C# 14) na máquina fictícia da Ana, na pasta ~/cs, com dotnet run arquivo.cs. Alguns leem o que a Ana digita, com Console.ReadLine() ("ler linha do console"): ele espera a pessoa digitar e apertar Enter. A aula Ler do Teclado explica tudo sobre ele.
A ideia: provar a sopa
A Ana está fazendo uma sopa. Ela prova, e só depois de provar sabe se precisa de mais sal. Se precisar, põe sal e prova de novo. Ela não tem como decidir antes da primeira colherada.
- Faz (prova a sopa).
- Testa ("está sem sal?").
- Se sim, volta ao passo 1. Se não, termina.
Compare com o while da louça da aula anterior: lá, o teste vinha antes ("tem louça?"), e com a pia vazia ela não lavava nada. Aqui, a primeira prova acontece sempre.
1. O primeiro do while
int i = 1;
do
{
Console.WriteLine($"Volta {i}");
i++;
} while (i <= 3);
Console.WriteLine("Fim!");
$ dotnet run tres.cs Volta 1 Volta 2 Volta 3 Fim!
- do
- "Faça": marca o começo do laço. Não tem condição aqui.
- { … }
- O corpo, que roda primeiro, sem pergunta nenhuma.
- while (i <= 3)
- A condição vem depois do corpo, logo depois da chave de fechamento. Se for
true, o programa volta para odo. - ;
- O ponto e vírgula no fim é obrigatório. É o único laço que termina com
;(nowhilecomum, ele seria um erro). A seção 5 mostra o que acontece sem ele.
Com um contador assim, o do while e o while dão o mesmo resultado. A diferença só aparece quando a condição já começa falsa.
2. A diferença: pelo menos uma vez
Os dois laços abaixo têm o mesmo corpo e a mesma condição, com o estoque começando em 0:
int estoque = 0;
while (estoque > 0)
{
Console.WriteLine("while: vendendo uma unidade...");
estoque--;
}
do
{
Console.WriteLine("do while: vendendo uma unidade...");
estoque--;
} while (estoque > 0);
Console.WriteLine($"Estoque final: {estoque}");
$ dotnet run comparar.cs do while: vendendo uma unidade... « o while não rodou; o do while rodou uma vez Estoque final: -1 « e vendeu o que não tinha!
O while testou 0 > 0, deu false, e pulou o corpo. O do while rodou o corpo primeiro, vendeu uma unidade que não existia (o estoque foi para −1) e só então testou. A lição vale nos dois sentidos:
while | do while | |
|---|---|---|
| Quando testa | Antes de cada volta | Depois de cada volta |
| Mínimo de voltas | 0 | 1 |
| Bom para | "Se tiver, faça" (vender do estoque, lavar a louça) | "Faça, depois veja se repete" (menu, pedir de novo, jogar de novo) |
Para vender do estoque, o while é o certo: com estoque zero, não se vende nada. O do while é para quando a primeira volta é obrigatória.
Missão: o jogo da Ana pergunta "Jogar de novo?" no fim de cada rodada, mas com o while ele não joga nem a primeira, porque jogarDeNovo começa false. Transforme o laço num do while. As respostas já estão preparadas: s, s e n. A última linha deve ser:
Fim de jogo depois de 3 rodadas.
int rodada = 0;
bool jogarDeNovo = false;
while (jogarDeNovo)
{
rodada++;
Console.WriteLine($"Rodada {rodada}: jogando...");
Console.Write("Jogar de novo? (s/n) ");
jogarDeNovo = Console.ReadLine() == "s";
}
Console.WriteLine($"Fim de jogo depois de {rodada} rodadas.");
int rodada = 0;
bool jogarDeNovo = false;
do
{
rodada++;
Console.WriteLine($"Rodada {rodada}: jogando...");
Console.Write("Jogar de novo? (s/n) ");
jogarDeNovo = Console.ReadLine() == "s";
} while (jogarDeNovo);
Console.WriteLine($"Fim de jogo depois de {rodada} rodadas.");
Na caixa, o que o programa "lê" vem da entrada preparada e não aparece na saída. No terminal, você veria o que digitou logo depois de cada pergunta, como na próxima seção.
3. Pedir de novo até a entrada ser válida
Este é o uso mais comum do do while. A escola da Ana só aceita notas de 0 a 10. O programa pergunta (sempre pelo menos uma vez) e, se a resposta não servir, pergunta de novo:
int nota;
bool valida;
do
{
Console.Write("Digite a nota (0 a 10): ");
valida = int.TryParse(Console.ReadLine(), out nota) && nota >= 0 && nota <= 10;
if (!valida)
{
Console.WriteLine("Nota inválida, tente de novo.");
}
} while (!valida);
Console.WriteLine($"Nota registrada: {nota}");
$ dotnet run nota.cs Digite a nota (0 a 10): 15 Nota inválida, tente de novo. « é número, mas passa de 10 Digite a nota (0 a 10): abc Nota inválida, tente de novo. « nem é número Digite a nota (0 a 10): 8 Nota registrada: 8
- int nota; bool valida;
- As duas variáveis são declaradas antes do
do, porque a condição e a última linha precisam delas (a seção 5 mostra por quê). - int.TryParse(Console.ReadLine(), out nota)
- Lê uma linha e tenta transformá-la em
int(aula Parse e TryParse). Dátruee guarda o número emnotase conseguir; dáfalsecom"abc". - && nota >= 0 && nota <= 10
- Além de ser número, tem de estar na faixa. Por causa do curto-circuito do
&&(aula Operadores Lógicos), a faixa só é conferida se oTryParsedeu certo. - while (!valida);
- "Repita enquanto não for válida". Quando a Ana digita 8,
validaviratrue,!validavirafalse, e o laço termina.
Com um while comum, você teria de escrever a pergunta e a leitura duas vezes: uma antes do laço e outra no fim do corpo. O do while evita essa repetição.
4. O menu que repete
O outro uso clássico: mostrar o menu, fazer o que a pessoa pediu e mostrar o menu de novo, até ela escolher "Sair". O menu tem de aparecer pelo menos uma vez, senão ela nem saberia as opções. Este programa junta o do while com o switch:
int opcao;
do
{
Console.WriteLine("1 - Ver saldo");
Console.WriteLine("2 - Fazer um Pix");
Console.WriteLine("0 - Sair");
Console.Write("Escolha: ");
int.TryParse(Console.ReadLine(), out opcao);
switch (opcao)
{
case 1:
Console.WriteLine("Saldo: R$ 350,00");
break;
case 2:
Console.WriteLine("Pix enviado!");
break;
case 0:
Console.WriteLine("Até logo!");
break;
default:
Console.WriteLine("Opção inválida.");
break;
}
} while (opcao != 0);
$ dotnet run menu.cs 1 - Ver saldo 2 - Fazer um Pix 0 - Sair Escolha: 1 Saldo: R$ 350,00 1 - Ver saldo 2 - Fazer um Pix 0 - Sair Escolha: 7 Opção inválida. 1 - Ver saldo 2 - Fazer um Pix 0 - Sair Escolha: 0 Até logo!
Repare que o break de cada case sai só do switch, não do laço: depois dele, o programa chega ao while (opcao != 0) e decide se mostra o menu de novo. Se a pessoa digitar algo que não é número, o TryParse põe 0 em opcao, e o programa sai; num programa de verdade, você trataria isso com um aviso.
5. Os dois erros típicos do do while
Esquecer o ; do fim
int i = 1;
do
{
Console.WriteLine(i);
i++;
} while (i <= 3)
Console.WriteLine("Fim!");
$ dotnet run semponto.cs /home/ana/cs/semponto.cs(6,17): error CS1002: ; esperado Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
"; esperado" na linha 6, coluna 17: logo depois do ). No do while, o while (…) é o fim da instrução, e toda instrução que não termina com chave termina com ;.
A variável criada no corpo não existe na condição
Parece natural criar a variável dentro do do, onde ela é lida:
do
{
Console.Write("Digite um número positivo: ");
int.TryParse(Console.ReadLine(), out int n);
} while (n <= 0);
$ dotnet run escopo.cs /home/ana/cs/escopo.cs(5,10): error CS0103: O nome "n" não existe no contexto atual Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O while (n <= 0) está fora das chaves do corpo, e a variável n, criada lá dentro (com o out int n), já sumiu quando a condição é testada. Conserto: declarar antes do do, como no programa da nota:
int n;
do
{
Console.Write("Digite um número positivo: ");
int.TryParse(Console.ReadLine(), out n);
} while (n <= 0);
Formas diferentes + qual usar
Pedir de novo até a entrada ser válida pode ser escrito de três jeitos. Os três funcionam:
| Forma | Como fica | Comentário |
|---|---|---|
do while | do { perguntar; ler; } while (!valida); | A pergunta aparece uma vez no código. É a forma natural. |
while | perguntar; ler; while (!valida) { avisar; perguntar; ler; } | A pergunta e a leitura ficam repetidas. |
while (true) + break | while (true) { perguntar; ler; if (valida) { break; } avisar; } | Bom quando a saída fica no meio do corpo. Aula break e continue. |
Na prática, o do while é o laço menos usado dos quatro (for, foreach e while aparecem muito mais), mas nos casos em que a primeira volta é obrigatória, ele deixa o código mais curto e mais claro.
Quando dá errado
error CS1002: ; esperado depois do while (…)
O que significa: faltou o ponto e vírgula no fim do do while.
Como consertar: } while (condição);.
error CS0103: O nome "n" não existe no contexto atual na condição
O que significa: a variável usada no while (…) foi criada dentro das chaves do do. A condição fica fora delas.
Como consertar: declare a variável antes do do e, lá dentro, só dê o valor.
error CS1003: Erro de sintaxe, "while" esperado
$ cat semwhile.cs
int i = 1;
do
{
Console.WriteLine(i);
i++;
}
Console.WriteLine("Fim!");
$ dotnet run semwhile.cs
/home/ana/cs/semwhile.cs(6,2): error CS1003: Erro de sintaxe, "while" esperado
/home/ana/cs/semwhile.cs(6,2): error CS1003: Erro de sintaxe, "(" esperado
/home/ana/cs/semwhile.cs(7,26): error CS1026: ) esperado
Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: o do não pode ficar sozinho: depois da chave de fechamento, o compilador espera o while. Os outros dois erros são consequência (ele tentou usar a linha seguinte como condição). O editor do guia mostra os mesmos três erros.
Como consertar: } while (condição); logo depois da chave.
O que significa: você usou do while num caso em que a primeira volta não é obrigatória, como vender do estoque (seção 2).
Como consertar: use while, que testa antes.
O que significa: laço infinito: a condição nunca fica false. No do while de entrada, acontece quando a condição está invertida (while (valida) no lugar de while (!valida)), ou quando nada no corpo muda a variável da condição.
Como consertar: Ctrl+C no terminal, e leia a condição em voz alta: "repita enquanto…". Veja também o timeout na aula while.
Referência rápida
| Forma | O que faz |
|---|---|
do { … } while (cond); | Roda o corpo, depois testa; repete enquanto cond for true. Pelo menos uma volta. |
do { pedir; } while (!valida); | Pede de novo até a entrada ser válida. |
do { menu; } while (opcao != 0); | Menu que repete até "Sair". |
O ; do fim | Obrigatório (CS1002 sem ele). |
| Variável da condição | Declarada antes do do (CS0103 se for dentro). |
Exercícios
1. Faça
Complete com a palavra que começa o laço:
2. O fim
Complete com o caractere que falta no fim:
3. Quantas voltas?
Quantas vezes o corpo roda? int x = 100; do { x++; } while (x < 5);
4. Qual é a diferença entre while e do while?
5. Para qual destas tarefas o do while é a melhor escolha?
Missão final: a nota válida
Missão: o programa pergunta a nota uma vez só e registra até o 15, que é inválido. Ponha a pergunta, a leitura e o aviso dentro de um do while que repete enquanto a nota não for válida. As respostas preparadas são 15, abc e 8. A saída deve terminar com:
Nota registrada: 8
int nota;
bool valida;
Console.Write("Digite a nota (0 a 10): ");
valida = int.TryParse(Console.ReadLine(), out nota) && nota >= 0 && nota <= 10;
if (!valida)
{
Console.WriteLine("Nota inválida, tente de novo.");
}
Console.WriteLine($"Nota registrada: {nota}");
int nota;
bool valida;
do
{
Console.Write("Digite a nota (0 a 10): ");
valida = int.TryParse(Console.ReadLine(), out nota) && nota >= 0 && nota <= 10;
if (!valida)
{
Console.WriteLine("Nota inválida, tente de novo.");
}
} while (!valida);
Console.WriteLine($"Nota registrada: {nota}");
No terminal, com dotnet run, teste também -1, 10 e 0: os limites da faixa.
Resumo
do { … } while (condição);roda o corpo primeiro e testa depois: sempre pelo menos uma volta.- Use quando a primeira volta é obrigatória: pedir de novo até a entrada ser válida, menus, "jogar de novo?". Para "se tiver, faça", use
while. - O
;no fim é obrigatório (CS1002), e odonão vive sem owhile(CS1003). - A condição fica fora das chaves: variáveis usadas nela são declaradas antes do
do(CS0103).