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.

Você vai aprender
  • Como escrever do { … } while (condição); e em que ordem ele roda.
  • A diferença para o while: o do while roda 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.

  1. Faz (prova a sopa).
  2. Testa ("está sem sal?").
  3. 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 o do.
;
O ponto e vírgula no fim é obrigatório. É o único laço que termina com ; (no while comum, 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:

whiledo while
Quando testaAntes de cada voltaDepois de cada volta
Mínimo de voltas01
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á true e guarda o número em nota se conseguir; dá false com "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 o TryParse deu certo.
while (!valida);
"Repita enquanto não for válida". Quando a Ana digita 8, valida vira true, !valida vira false, 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:

FormaComo ficaComentário
do whiledo { perguntar; ler; } while (!valida);A pergunta aparece uma vez no código. É a forma natural.
whileperguntar; ler; while (!valida) { avisar; perguntar; ler; }A pergunta e a leitura ficam repetidas.
while (true) + breakwhile (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 corpo rodou uma vez quando não devia

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 programa não termina

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

FormaO 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 fimObrigatório (CS1002 sem ele).
Variável da condiçãoDeclarada antes do do (CS0103 se for dentro).

Exercícios

1. Faça

Complete com a palavra que começa o laço:

{ Console.WriteLine(i); i++; } while (i <= 3);

2. O fim

Complete com o caractere que falta no fim:

do { i++; } while (i < 10)

3. Quantas voltas?

Quantas vezes o corpo roda? int x = 100; do { x++; } while (x < 5);

Voltas:

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 o do não vive sem o while (CS1003).
  • A condição fica fora das chaves: variáveis usadas nela são declaradas antes do do (CS0103).