Instruções de iteração

Tip

Este artigo faz parte da seção Conceitos Básicos para desenvolvedores que já conhecem pelo menos uma linguagem de programação e estão aprendendo C#. Se você é novo em programação, comece com os tutoriais Comece agora. Para ver a sintaxe completa, consulte as instruções de iteração na referência da linguagem.

Vindo de outro idioma? Todos os quatro loops C# (foreach, while,do-while e for) têm equivalentes diretos em Java, C++e JavaScript. Você usa foreach com mais frequência. Itera sobre uma coleção sem índice, como o for aprimorado do Java ou o for...of do JavaScript.

As instruções de iteração executam um bloco de código repetidamente. Cada passagem pelo bloco é uma iteração e um bloco repetido é um loop. O C# fornece quatro loops. Comece com foreach para coleções, use while e do-while quando uma condição controla a repetição, e use for quando precisar de um índice explícito.

Iterar uma coleção com foreach

A instrução foreach executa o corpo uma vez para cada elemento em uma coleção, em ordem. É a escolha mais comum para percorrer uma coleção porque você não precisa gerenciar um índice nem fazer a verificação dos limites. A instrução foreach impede erros off-by-one típicos:

string[] names = ["Ana", "Ben", "Cleo"];

// foreach reads each element in order. It's the default loop for
// collections: no index to manage and no off-by-one mistakes.
foreach (string name in names)
{
    Console.WriteLine(name); // => Ana, then Ben, then Cleo
}

foreach funciona com qualquer tipo que O C# reconhece como uma sequência, incluindo matrizes, List<T>e Dictionary<TKey,TValue>. A variável de iteração (name no exemplo anterior) é somente leitura, portanto, você não pode reatribuí-la dentro do loop.

O corpo de um loop é uma única instrução, como uma atribuição ou uma chamada de método. Uma instrução de bloco é, por si só, uma única instrução que delimita zero ou mais instruções entre chaves ({ }).

Muitos padrões de codificação recomendam que você delimite o corpo do loop com chaves, mesmo para uma única instrução. As chaves tornam o escopo explícito. Ele evita um erro comum: adicionar depois uma segunda linha que você espera que seja executada a cada iteração, mas que, em vez disso, é executada uma única vez depois do loop. Somente as chaves decidem quais instruções pertencem ao loop. C# não trata os espaços em branco como relevantes, portanto a indentação, por si só, nunca é suficiente. Chaves são legais mesmo em torno de uma linha: o bloco é a única instrução que o loop repete. Recue seu código para melhorar a legibilidade, mas use as chaves para definir o bloco.

Repita enquanto uma condição se mantém com while

Um while loop verifica sua condição booliana antes de cada iteração. Se a condição estiver false no início, o corpo nunca será executado, portanto, um loop while é executado zero ou mais vezes:

int countdown = 3;

// A while loop checks its condition before each iteration, so the body
// runs zero or more times.
while (countdown > 0)
{
    Console.WriteLine(countdown); // => 3, then 2, then 1
    countdown--;
}

Verifique se algo dentro do loop altera a condição. No exemplo anterior, countdown-- eventualmente faz a condição false. Um loop cuja condição nunca se torna false é executado para sempre.

Execute o bloco pelo menos uma vez com do-while

Um loop do-while verifica a condição após cada iteração, de modo que o corpo sempre é executado pelo menos uma vez. Use-o quando a primeira execução precisar ocorrer antes que você possa avaliar a condição, como ao solicitar uma entrada e depois validá-la:

int attempts = 0;

// A do-while loop runs its body once, then checks the condition. Use it
// when the body must run at least one time.
do
{
    attempts++;
    Console.WriteLine($"Attempt {attempts}"); // => Attempt 1, then Attempt 2, then Attempt 3
}
while (attempts < 3);

Contar com for

Uma for instrução loop contém três partes: um inicializador que é executado uma vez antes do loop, uma condição que é verificada antes de cada iteração e um iterador que é executado após cada iteração. Use for quando precisar do índice em si, não apenas os elementos. Normalmente, você precisa do índice quando deseja modificar o elemento em vez de ler seu valor.

// A for loop fits when you need an explicit index. The three parts are
// the initializer, the condition, and the iterator.
for (int i = 0; i < 3; i++)
{
    Console.WriteLine(i); // => 0, then 1, then 2
}

Quando você apenas lê os elementos, sem usar as posições nem atribuir novos valores, prefira foreach. Ele indica a intenção com mais clareza e evita a aritmética do índice.

Sair ou ignorar com break e continue

Duas instruções de salto oferecem um controle mais fino dentro de qualquer loop. A break instrução sai do loop imediatamente, ignorando todas as iterações restantes:

int[] numbers = [2, 4, 7, 8];

// break stops the loop immediately, skipping any remaining elements.
foreach (int number in numbers)
{
    if (number % 2 != 0)
    {
        Console.WriteLine($"First odd number: {number}"); // => First odd number: 7
        break;
    }
}

A continue instrução ignora o restante da iteração atual e passa para a próxima:

int[] values = [1, 2, 3, 4, 5];

// continue skips the rest of the current iteration and moves to the next.
foreach (int value in values)
{
    if (value % 2 == 0)
    {
        continue; // skip even numbers
    }

    Console.WriteLine(value); // => 1, then 3, then 5
}

Iterar um fluxo assíncrono com await foreach

Um fluxo assíncrono é um leitor que usa uma tarefa assíncrona para produzir cada próximo elemento. O C# representa-o com a IAsyncEnumerable<T> interface. Os dados que chegam ao longo do tempo, como páginas de uma API Web ou linhas de um banco de dados, se ajustam a esse modelo: recuperar o próximo elemento é uma operação aguardada em vez de um retorno imediato.

Para consumir um fluxo assíncrono, coloque a await palavra-chave antes foreach. Cada iteração aguarda o próximo elemento, de modo que o loop seja suspenso enquanto esse elemento é produzido em vez de bloquear o thread:

private static async Task AwaitForeachExample()
{
    // await foreach consumes an asynchronous stream. Each iteration can
    // suspend while the next element is produced.
    await foreach (int value in GenerateAsync())
    {
        Console.WriteLine(value); // => 0, then 1, then 2
    }
}

private static async IAsyncEnumerable<int> GenerateAsync()
{
    for (int i = 0; i < 3; i++)
    {
        await Task.Delay(1); // stand-in for real asynchronous work
        yield return i;
    }
}

Os fluxos assíncronos se baseiam em async e await. Para obter um passo a passo completo, consulte Gerar e consumir fluxos assíncronos.

Consulte também