C# break e continue

Até aqui, todo laço ia do começo ao fim: dava todas as voltas até a condição ficar falsa ou a lista acabar. Mas às vezes você quer parar antes (achou o que procurava, não precisa olhar o resto) ou pular uma volta (este item não serve, passe para o próximo). Para isso existem duas palavras: break ("quebrar"), que sai do laço na hora, e continue ("continuar"), que pula o resto da volta e vai para a próxima.

Você vai aprender
  • Como usar break para sair de um laço assim que der (uma busca, por exemplo).
  • Como usar continue para pular os itens que não servem.
  • O padrão while (true) + break, com entrada do teclado.
  • A armadilha do continue que pula o i++.
  • O que o break faz dentro de um switch que está dentro de um laço, e em laços aninhados.
  • Os erros CS0139 e o aviso CS0162.

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.


A ideia: procurar a chave e separar as frutas

A Ana procura a chave de casa numa gaveta cheia de coisas. Ela tira um objeto de cada vez. Quando acha a chave, ela para: não faz sentido olhar o resto da gaveta. Isso é o break.

Depois, na feira, ela escolhe laranjas uma por uma. Se uma está machucada, ela deixa de lado e pega a próxima, sem parar de escolher. Isso é o continue.

breakcontinue
O que fazSai do laço inteiroPula o resto desta volta
Depois deleO programa segue na linha depois do laçoO laço vai para a próxima volta (no for, roda o passo antes)
ComparaçãoAchou a chave: para de procurarLaranja machucada: pega a próxima

1. break: parar quando achar

A lojinha da Ana tem os produtos num array, e ela quer saber em que posição está a borracha:

string[] estoque = ["caneta", "caderno", "borracha", "lápis", "régua"];
string procurado = "borracha";
int posicao = -1;
for (int i = 0; i < estoque.Length; i++)
{
    Console.WriteLine($"Olhando a posição {i}: {estoque[i]}");
    if (estoque[i] == procurado)
    {
        posicao = i;
        break;
    }
}
if (posicao >= 0)
{
    Console.WriteLine($"Achei \"{procurado}\" na posição {posicao}.");
}
else
{
    Console.WriteLine($"Não temos \"{procurado}\".");
}
$ dotnet run procura.cs
Olhando a posição 0: caneta
Olhando a posição 1: caderno
Olhando a posição 2: borracha      « achou: o break sai do for
Achei "borracha" na posição 2.     « "lápis" e "régua" nem foram olhados
int posicao = -1;
Um valor que "não é posição nenhuma", para saber, depois do laço, se achou ou não. É um truque muito comum.
if (estoque[i] == procurado)
O break quase sempre fica dentro de um if: "se achou, pare".
break;
Sai do for na hora. O passo (i++) não roda mais, nem a condição é testada de novo. O programa pula para a primeira linha depois do laço, o if (posicao >= 0).

Troque "borracha" por "tesoura": o laço olha as 5 posições, não acha, termina normalmente, e o programa mostra Não temos "tesoura".. O break não muda o resultado da busca; ele só evita trabalho inútil. Num array de 5 itens isso não faz diferença, mas num de 1 milhão faz.

Missão: o programa deveria mostrar só o primeiro preço acima de R$ 100, mas mostra todos. Acrescente um break no lugar certo.

Primeiro preço acima de R$ 100: R$ 129,90
decimal[] precos = [45.00m, 89.90m, 129.90m, 15.50m, 210.00m];
foreach (decimal preco in precos)
{
    if (preco > 100m)
    {
        Console.WriteLine($"Primeiro preço acima de R$ 100: R$ {preco:N2}");
    }
}
decimal[] precos = [45.00m, 89.90m, 129.90m, 15.50m, 210.00m];
foreach (decimal preco in precos)
{
    if (preco > 100m)
    {
        Console.WriteLine($"Primeiro preço acima de R$ 100: R$ {preco:N2}");
        break;
    }
}

2. while (true) + break

Na aula do while, você viu o while (true), um laço que nunca para sozinho. Com break, ele vira um jeito muito claro de escrever "repita até…", quando a decisão de sair fica no meio do corpo. A Ana soma números digitados até digitar 0 (o Console.ReadLine() lê o que a pessoa digita; aula Ler do Teclado):

int soma = 0;
while (true)
{
    Console.Write("Número (0 para terminar): ");
    if (!int.TryParse(Console.ReadLine(), out int n))
    {
        Console.WriteLine("Isso não é um número.");
        continue;
    }
    if (n == 0)
    {
        break;
    }
    soma += n;
}
Console.WriteLine($"Soma: {soma}");
$ dotnet run somar.cs
Número (0 para terminar): 10
Número (0 para terminar): 25
Número (0 para terminar): abc
Isso não é um número.               « o continue voltou para a pergunta
Número (0 para terminar): 7
Número (0 para terminar): 0         « o break saiu do laço
Soma: 42
  1. Pergunta e lê. Sempre, pelo menos uma vez (como no do while).
  2. Não é número? Avisa e continue: pula o resto da volta e pergunta de novo. O "abc" não entrou na soma.
  3. É zero? break: sai do laço, e o programa mostra a soma.
  4. Senão, soma e volta ao passo 1.

Os dois aparecem no mesmo programa, cada um com o seu papel. Sem o break, esse laço seria infinito; por isso, todo while (true) precisa de pelo menos um caminho que chegue a um break.


3. continue: pular o que não serve

O continue pula o resto do corpo e vai direto para a próxima volta. Aqui, ele pula os números ímpares:

for (int n = 1; n <= 10; n++)
{
    if (n % 2 != 0)
    {
        continue;
    }
    Console.Write($"{n} ");
}
Console.WriteLine();
$ dotnet run pares.cs
2 4 6 8 10 

Num for, o continue vai para o passo (n++) e depois para a condição, como no fim de qualquer volta. (Para só os pares, n += 2 seria mais simples; o exemplo é para mostrar o continue.)

Um uso mais realista: ignorar dados ruins. A professora digitou algumas notas erradas, e elas não podem entrar na média:

double[] notas = [8.0, -1.0, 6.5, 11.0, 9.5];
double soma = 0;
int validas = 0;
foreach (double nota in notas)
{
    if (nota < 0 || nota > 10)
    {
        Console.WriteLine($"Ignorando a nota inválida {nota}");
        continue;
    }
    soma += nota;
    validas++;
}
Console.WriteLine($"Média de {validas} notas: {soma / validas:N2}");
$ dotnet run notas.cs
Ignorando a nota inválida -1
Ignorando a nota inválida 11
Média de 3 notas: 8,00

Esse estilo se chama cláusula de guarda: os casos que não servem são descartados logo no começo do corpo, com continue, e o resto do corpo cuida só dos casos bons, sem um if enorme em volta. Dava para escrever o mesmo com if (nota >= 0 && nota <= 10) { … }; quando há várias regras de descarte, o continue deixa o código mais "reto".


4. A armadilha: continue no while

No for, o continue sempre passa pelo passo. No while, o passo é uma linha comum do corpo, e o continue pode pulá-la. A Ana quis mostrar de 0 a 9, pulando o 5:

int i = 0;
while (i < 10)
{
    if (i == 5)
    {
        continue;
    }
    Console.Write($"{i} ");
    i++;
}
Console.WriteLine("Fim!");
$ timeout 15 dotnet run continfinito.cs
0 1 2 3 4 $ echo $?
124
  1. De 0 a 4, tudo certo: mostra o número e faz i++.
  2. Com i = 5, o continue pula o Console.Write e o i++.
  3. Na próxima volta, i ainda vale 5, e o continue pula de novo. E de novo. Para sempre: o timeout encerrou o programa depois de 15 segundos (código 124, aula while).

O $ echo $? apareceu na mesma linha dos números porque o programa nunca chegou a pular linha: o Fim! não foi mostrado. Dois consertos: fazer o i++ antes do continue (if (i == 5) { i++; continue; }), ou, melhor ainda, usar um for, em que o passo nunca é pulado:

for (int i = 0; i < 10; i++)
{
    if (i == 5)
    {
        continue;
    }
    Console.Write($"{i} ");
}

5. Qual laço o break quebra?

Dentro de um switch

Lembra que cada case do switch termina com break? Esse break sai do switch, não do laço em volta. Este programa simula um menu com as escolhas já guardadas num array:

int[] escolhas = [1, 2, 0, 1];
foreach (int opcao in escolhas)
{
    switch (opcao)
    {
        case 1:
            Console.WriteLine("Ver saldo");
            break;
        case 2:
            Console.WriteLine("Fazer um Pix");
            break;
        case 0:
            Console.WriteLine("Sair");
            break;
    }
}
Console.WriteLine("Fim do programa.");
$ dotnet run menu.cs
Ver saldo
Fazer um Pix
Sair
Ver saldo          « escolheu "Sair", mas o laço continuou!
Fim do programa.

O break do case 0 só saiu do switch. Para sair do laço também, uma saída simples é uma variável bool que o case liga e que o laço confere depois do switch:

int[] escolhas = [1, 2, 0, 1];
bool sair = false;
foreach (int opcao in escolhas)
{
    switch (opcao)
    {
        case 1:
            Console.WriteLine("Ver saldo");
            break;
        case 2:
            Console.WriteLine("Fazer um Pix");
            break;
        case 0:
            Console.WriteLine("Sair");
            sair = true;
            break;
    }
    if (sair)
    {
        break;
    }
}
Console.WriteLine("Fim do programa.");
$ dotnet run menu2.cs
Ver saldo
Fazer um Pix
Sair
Fim do programa.

Num menu de verdade, com do while (opcao != 0) como na aula do while, nem precisa disso: a condição do laço já cuida da saída.

Laços um dentro do outro

Com um laço dentro de outro (assunto da próxima aula, Laços Aninhados), o break sai só do laço mais de dentro, aquele em que ele está:

for (int linha = 1; linha <= 3; linha++)
{
    for (int coluna = 1; coluna <= 3; coluna++)
    {
        if (coluna == 2)
        {
            break;
        }
        Console.Write($"({linha},{coluna}) ");
    }
    Console.WriteLine($"fim da linha {linha}");
}
$ dotnet run aninhado.cs
(1,1) fim da linha 1
(2,1) fim da linha 2
(3,1) fim da linha 3

O break interrompeu o laço das colunas (que parou na coluna 2), mas o laço das linhas continuou normalmente. O mesmo vale para o continue: ele vai para a próxima volta do laço mais de dentro.

Sair de tudo: return

Nos programas de arquivo único do guia, return; encerra o programa inteiro, de dentro de quantos laços forem (dentro de um método, ele sai do método; aula Valor de Retorno):

int[] escolhas = [1, 0, 2];
foreach (int opcao in escolhas)
{
    if (opcao == 0)
    {
        Console.WriteLine("Saindo do programa.");
        return;
    }
    Console.WriteLine($"Opção {opcao}");
}
Console.WriteLine("Esta linha não aparece.");
$ dotnet run retorno.cs
Opção 1
Saindo do programa.

Usar ou não usar?

Alguns professores dizem para evitar break e continue, porque um laço com muitas saídas fica difícil de seguir. A regra prática:

SituaçãoBom uso?
Busca: parar quando acharSim, é o uso clássico do break.
while (true) com a saída no meio (ler até digitar 0)Sim, quando a condição de saída só pode ser testada no meio do corpo.
Descartar itens ruins no começo do corpoSim, o continue como cláusula de guarda.
Vários break e continue espalhados pelo corpoNão: reescreva a condição do laço ou divida em partes (mais tarde, em métodos).
Um break que poderia ser a condição do laçoPrefira a condição: while (i < 10) em vez de while (true) { if (i >= 10) break; … }.

Quando dá errado

error CS0139: Nenhum loop delimitador a partir do qual quebrar ou continuar
$ cat fora.cs
int idade = 15;
if (idade < 18)
{
    Console.WriteLine("Menor de idade.");
    break;
}
$ dotnet run fora.cs
/home/ana/cs/fora.cs(5,5): error CS0139: Nenhum loop delimitador a partir do qual quebrar ou continuar

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

O que significa: "não há nenhum laço em volta para sair dele ou continuar" (No enclosing loop out of which to break or continue). O break e o continue só existem dentro de laços (o break também dentro de um switch). Um if não é laço: não se "sai" de um if.

Como consertar: se a ideia era pular o resto do if, use else. Se era encerrar o programa, use return;. Um continue num switch sem laço em volta dá o mesmo CS0139 (e mais um CS8070, porque o caso ficou sem break).

warning CS0162: Código inacessível detectado
$ cat depois.cs
for (int i = 1; i <= 3; i++)
{
    break;
    Console.WriteLine(i);
}
$ dotnet run depois.cs
/home/ana/cs/depois.cs(4,5): warning CS0162: Código inacessível detectado

O que significa: há uma linha logo depois de um break (ou continue, ou return) no mesmo bloco. Ela nunca vai rodar. O programa rodou e não mostrou nada: o break saiu na primeira volta.

Como consertar: o break quase sempre deve estar dentro de um if. Coloque a condição que faltou, ou mova a linha para antes do break.

O laço trava depois de um continue

O que significa: num while ou do while, o continue pulou a linha que muda a variável da condição (seção 4).

Como consertar: faça o passo antes do continue, ou use um for.

O break não saiu do laço

O que significa: ele estava dentro de um switch (e saiu só do switch) ou de um laço interno (e saiu só dele).

Como consertar: uma variável bool conferida depois (seção 5), a condição do próprio laço, ou return.


Referência rápida

InstruçãoO que faz
break;Sai do laço (ou switch) mais de dentro, na hora.
continue;Pula o resto da volta e vai para a próxima (no for, passando pelo passo).
if (achou) { break; }Para uma busca.
if (ruim) { continue; }Descarta um item (cláusula de guarda).
while (true) { … if (fim) { break; } … }Repetir até uma condição testada no meio.
return;Sai de tudo (do programa, ou do método).
Fora de laçoErro CS0139.

Exercícios

1. Sair do laço

Complete com a palavra que sai do laço quando acha o número 7:

foreach (int n in numeros) { if (n == 7) { ; } }

2. Pular a volta

Complete com a palavra que pula os números negativos:

foreach (int n in numeros) { if (n < 0) { ; } Console.WriteLine(n); }

3. O que aparece?

O que este laço mostra? for (int i = 1; i <= 5; i++) { if (i == 3) { break; } Console.Write(i); }

Saída:

4. E com continue no lugar do break? for (int i = 1; i <= 5; i++) { if (i == 3) { continue; } Console.Write(i); }

5. Um break dentro de um case de um switch, que está dentro de um foreach, sai de quê?

6. O que acontece com if (x > 0) { break; } fora de qualquer laço?

Missão final: as leituras do sensor

Missão: o termômetro da estufa da Ana manda uma lista de leituras. Duas regras: uma leitura negativa é erro do sensor e deve ser pulada (continue); a leitura 999 quer dizer "fim da transmissão", e tudo depois dela deve ser ignorado (break). Acrescente as duas regras no começo do corpo do foreach. A saída deve ser:

Leituras válidas: 4, soma: 74
int[] leituras = [12, -1, 30, 7, -5, 25, 999, 40, 3];
int validas = 0;
int soma = 0;
foreach (int leitura in leituras)
{
    // as duas regras aqui

    validas++;
    soma += leitura;
}
Console.WriteLine($"Leituras válidas: {validas}, soma: {soma}");
int[] leituras = [12, -1, 30, 7, -5, 25, 999, 40, 3];
int validas = 0;
int soma = 0;
foreach (int leitura in leituras)
{
    if (leitura == 999)
    {
        break;
    }
    if (leitura < 0)
    {
        continue;
    }
    validas++;
    soma += leitura;
}
Console.WriteLine($"Leituras válidas: {validas}, soma: {soma}");

As leituras válidas são 12, 30, 7 e 25. O 40 e o 3 vêm depois do 999 e ficam de fora. Experimente tirar o break e ver a soma mudar (o 999 entra na conta!).


Resumo

  • break sai do laço na hora; o programa segue depois dele. Uso clássico: parar uma busca quando achar.
  • continue pula o resto da volta e vai para a próxima. Uso clássico: descartar itens que não servem, no começo do corpo.
  • while (true) + break é o jeito de repetir até uma condição que só dá para testar no meio do corpo.
  • No while, cuidado: o continue pode pular o passo e travar o laço. No for, o passo sempre roda.
  • O break sai só do laço (ou switch) mais de dentro. Para sair de tudo, return.
  • Fora de um laço, break e continue dão CS0139; uma linha logo depois deles dá o aviso CS0162.