Um laço while repete o corpo enquanto uma condição continuar verdadeira. Use-o quando você não sabe de antemão quantas iterações vai precisar: continuar até um saldo atingir uma meta, uma fila ficar vazia ou a entrada acabar.
Saída:
Year 1: 1070.00
Year 2: 1144.90
Year 3: 1225.04
Year 4: 1310.80
Year 5: 1402.55
Year 6: 1500.73
Goal reached after 6 years
A condição é verificada antes de cada iteração, inclusive a primeira. Se savings já tivesse atingido a meta, o corpo nunca executaria e o programa imprimiria "after 0 years". O laço depende de o corpo mudar algo que a condição lê; aqui savings cresce a cada ano até savings < goal ficar falso.
do while: executa pelo menos uma vez
Um laço do while move o teste para o fim, então o corpo sempre executa pelo menos uma vez:
Saída:
Attempt 1: got 7
Attempt 2: got 4
Attempt 3: got 1
Done after 3 attempts
O primeiro valor de code só existe depois que o corpo executou uma vez, que é exatamente quando um do while se encaixa. Dois detalhes de sintaxe pegam muita gente:
- O ponto e vírgula depois de
while (code != 1)é obrigatório. Esquecê-lo é um erro de compilação. - As variáveis usadas na condição precisam ser declaradas antes do
do. Uma variável declarada dentro das chaves está fora de escopo quando a condição executa, entãodo { int code = ...; } while (code != 1);não compila.
O mesmo laço escrito com while precisa de uma primeira chamada duplicada antes do laço ou de um valor inicial fictício que force a primeira passada. O do while expressa diretamente "tente e depois decida se vai tentar de novo".
Lendo a entrada até um sentinela
Ler linhas até uma palavra de parada, ou até a entrada acabar, é o laço while clássico de livro. Console.ReadLine() retorna null quando não há mais entrada, então teste as duas coisas:
Saída:
skipping "abc": not a number
3 amounts, total 49.50
(line = Console.ReadLine()) != null atribui a próxima linha e a testa em uma única expressão; os parênteses em volta da atribuição são obrigatórios porque != tem precedência maior que =. O laço para em done, então o 99 depois dele nunca é lido. decimal.TryParse rejeita abc sem lançar exceção, e continue pula para a próxima linha. Sem a verificação de null, chegar ao fim da entrada passaria null para TryParse para sempre, já que ReadLine continua retornando null.
Um do while combina com o padrão de pedir e validar, em que a pergunta precisa aparecer pelo menos uma vez:
Saída:
Enter your age:
"-4" is not a valid age
Enter your age:
"fifteen" is not a valid age
Enter your age:
Age accepted: 15
A pergunta está dentro do laço, então ela se repete depois de cada resposta rejeitada, e age é declarada antes do do para que o código depois do laço possa usá-la. Com um usuário no teclado, cada resposta aparece no console depois da pergunta; com a entrada redirecionada, como aqui, só a saída do próprio programa é mostrada.
while (true) com break
Às vezes o ponto natural de saída fica no meio do corpo, depois de parte do trabalho e antes do resto. Escreva o laço como while (true) e saia com break:
Saída:
processing: resize photo
processing: send email
stop signal received
1 job(s) left in the queue
Dequeue tira o próximo trabalho da frente da fila, e o laço sai assim que vê o marcador de parada, então o último trabalho fica na fila. while (true) não é um mau sinal quando a saída é clara e fica perto do começo do corpo. Passa a ser quando há vários break espalhados por um corpo longo; aí uma condição bool com nome ou um método com return é mais fácil de acompanhar. A página de break e continue mostra como sair de laços aninhados.
Laços que nunca terminam
Um laço infinito sem querer tem uma de três causas.
O corpo nunca muda a condição. O contador nunca é incrementado, ou a variável errada é:
int i = 0;
while (i < 5)
{
Console.WriteLine(i); // i++ forgotten: prints 0 forever
}
Um ponto e vírgula depois da condição. O ponto e vírgula é o corpo inteiro do laço, e o bloco abaixo dele executa depois do laço (que nunca termina):
while (retries < 3); // warning CS0642: Possible mistaken empty statement
{
retries++;
}
Uma condição que passa do alvo. Testar != quando a variável anda em passos pode pular o valor de parada. while (x != 10) x += 3; vai 0, 3, 6, 9, 12 e passa direto do 10 (só cai no 10 por acaso, depois que o int estoura e dá a volta bilhões de passos depois). Teste com < ou > em vez de != sempre que o passo não for 1. O mesmo vale para valores double, que raramente acertam um alvo exato: while (x != 1.0) x += 0.1; nunca termina.
Escolhendo entre while, do while e for
| Situação | Laço |
|---|---|
| Número de iterações conhecido, ou um índice | for |
| Cada elemento de uma coleção | foreach |
| Repetir enquanto uma condição vale, possivelmente zero vezes | while |
| O corpo precisa executar uma vez antes de a condição fazer sentido | do while |
| Teste de saída no meio do corpo | while (true) com break |
Qualquer laço for pode ser reescrito como um laço while e vice-versa; a escolha é sobre qual deles coloca a lógica do laço onde o leitor espera. Quando o contador, o teste e o passo existem, o for os mantém em uma linha. Quando o laço é guiado por um estado que muda dentro do corpo, o while deixa isso claro.
Perguntas frequentes
Qual a diferença entre while e do while em C#?
Um laço while verifica a condição antes de cada iteração, então o corpo pode executar zero vezes. Um laço do { } while (condition); verifica depois de cada iteração, então o corpo sempre executa pelo menos uma vez. Use do while quando a primeira passada produz o valor que a condição testa, como ler uma entrada e depois validá-la.
Qual é a sintaxe de um laço do while em C#?
do { body } while (condition);. Repare no ponto e vírgula depois do parêntese de fechamento: ele é obrigatório, e esquecê-lo é um erro de compilação. A condição pode usar variáveis atribuídas no corpo, mas elas precisam ser declaradas antes do do, porque as chaves do corpo são um escopo separado.
Como escrever while (true) em C#?
while (true) { ... } repete para sempre até que algo dentro dele saia com break, return ou uma exceção. É a forma natural quando o teste de saída fica no meio do corpo, como ler uma linha e parar quando ela estiver vazia.
Como ler a entrada até o usuário digitar uma palavra ou a entrada acabar em C#?
Faça o laço sobre Console.ReadLine(), que retorna null no fim da entrada: string line; while ((line = Console.ReadLine()) != null && line != "done") { ... }. A atribuição dentro da condição lê a próxima linha e a testa em um só passo.
Por que meu laço while em C# nunca termina?
Normalmente o corpo nunca muda o que a condição testa (um i++ esquecido), ou há um ponto e vírgula logo depois da condição: while (count < 10); é um laço com corpo vazio que gira para sempre. O compilador avisa sobre o segundo caso com CS0642, Possible mistaken empty statement.