Instruções de iteração

Sugestão

Este artigo faz parte da secção Fundamentos para programadores que já conhecem pelo menos uma linguagem de programação e estão a aprender C#. Se és novo na programação, começa primeiro pelos tutoriais para começar . Para a sintaxe completa, veja instruções de iteração na referência da linguagem.

Vem de outra língua? Todos os quatro ciclos C# (foreach, while,do-while , e for) têm equivalentes diretos em Java, C++ e JavaScript. Usas foreach mais vezes. Ele itera uma coleção sem um índice, como o enhanced for do Java ou o do JavaScript for...of.

As instruções de iteração executam repetidamente um bloco de código. Cada passagem pelo bloco é uma iteração, e um bloco que se repete é um ciclo. C# tem quatro ciclos. Comece por foreach para coleções, use while edo-whilequando 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 respetivo corpo uma vez por cada elemento de uma coleção, pela ordem. É a escolha mais comum para ler uma coleção, porque não tens de gerir um índice nem fazer a verificação dos limites. A foreach instrução previne erros típicos de desligamento por um:

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 C# reconheça como uma sequência, incluindo arrays, List<T>, e Dictionary<TKey,TValue>. A variável de iteração (name no exemplo anterior) é só de leitura, por isso não podes reatribui-la dentro do ciclo.

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

Muitos padrões de codificação recomendam que o corpo do ciclo seja delimitado por chavetas, mesmo que contenha apenas uma única instrução. As chavetas tornam o âmbito explícito. Evita um erro comum: adicionar posteriormente uma segunda linha que se espera que seja executada em cada iteração, mas que, em vez disso, é executada uma única vez após o ciclo. Só as chavetas decidem que instruções pertencem ao ciclo. O C# não trata o espaço em branco como algo significativo, por isso a indentação sozinha nunca o faz. As chaves são válidas mesmo com uma única linha: o bloco é a única instrução que o ciclo repete. Indente o código para facilitar a leitura, mas use as chavetas para definir o bloco.

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

Um while ciclo verifica a sua condição booleana antes de cada iteração. Se a condição for false no início, o corpo nunca é executado, por isso um ciclo while executa-se 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--;
}

Certifica-te de que algo dentro do circuito altera a condição. No exemplo anterior, countdown-- acaba por tornar a condição false verdadeira. Um ciclo cuja condição nunca se torna false executa-se indefinidamente.

Passa pelo corpo pelo menos uma vez com do-while

Um do-while ciclo verifica a condição após cada iteração, por isso o corpo é sempre executado pelo menos uma vez. Utilize-o quando a primeira iteração tiver de ocorrer antes de poder avaliar a condição, como ao solicitar dados e depois validá-los:

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 corre uma vez antes do loop, uma condição verificada antes de cada iteração, e um iterador que corre após cada iteração. Usa for quando precisares do índice em si, não só dos elementos. Normalmente, precisas do índice quando queres modificar o elemento em vez de ler o 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 apenas lês os elementos, quando não usas as posições ou não atribuis novos valores, prefere foreach. Declara a intenção de forma mais clara e evita a aritmética indexada.

Sair ou saltar com break e continue

Duas instruções de salto permitem um controlo mais preciso dentro de qualquer ciclo. A break instrução sai imediatamente do ciclo, saltando quaisquer 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 salta o resto da iteração atual e passa para a seguinte:

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 utiliza uma tarefa assíncrona para produzir cada elemento seguinte. C# representa isso com a IAsyncEnumerable<T> interface. Os dados que chegam ao longo do tempo, como páginas de uma API web ou linhas de uma base de dados, encaixam-se neste modelo: recuperar o próximo elemento é uma operação aguardável em vez de um retorno imediato.

Para consumir um fluxo assíncrono, coloque a palavra-chave await antes de foreach. Cada iteração aguarda o próximo elemento, por isso o loop suspende-se 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 baseiam-se em async e await. Para uma explicação completa, veja Gerar e consumir fluxos assíncronos.

Ver também