Instrukcje iteracyjne

Wskazówka

Ten artykuł jest częścią sekcji Podstawy dla deweloperów, którzy już znają co najmniej jeden język programowania i uczą się języka C#. Jeśli dopiero zaczynasz programować, najpierw zacznij od samouczków Wprowadzenie . Aby uzyskać pełną składnię, zobacz instrukcje iteracji w informacjach o języku.

Pochodzi z innego języka? Wszystkie cztery pętle języka C# (foreach, while,do-while i for) mają bezpośrednie odpowiedniki w Java, C++i JavaScript. Najczęściej używasz foreach. Iteruje po kolekcji bez indeksu, jak rozszerzonego for w Javie lub for...of w JavaScripcie.

Instrukcje iteracji uruchamiają blok kodu wielokrotnie. Każde przejście przez blok jest iteracją, a powtarzany blok jest pętlą. Język C# udostępnia cztery pętle. Zacznij od foreach w przypadku kolekcji, użyj while oraz do-while, gdy warunek steruje powtarzaniem, a for użyj, gdy potrzebujesz jawnego indeksu.

Iterowanie kolekcji za pomocą polecenia foreach

Instrukcja foreach uruchamia treść raz dla każdego elementu w kolekcji w kolejności. Jest to najczęstszy wybór podczas odczytywania kolekcji, ponieważ nie zarządzasz indeksem ani sprawdzaniem granic. Instrukcja foreach zapobiega typowym błędom poza jednym:

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 współpracuje z dowolnym typem rozpoznawanym przez język C# jako sekwencją, w tym tablicami, List<T>i Dictionary<TKey,TValue>. Zmienna iteracji (name w poprzednim przykładzie) jest tylko do odczytu, więc nie można jej ponownie przypisać wewnątrz pętli.

Treść pętli jest pojedynczą instrukcją, taką jak przypisanie lub wywołanie metody. Instrukcja złożona sama w sobie jest pojedynczą instrukcją, która obejmuje zero lub więcej instrukcji ujętych w nawiasy klamrowe ({ }).

Wiele standardów kodowania zaleca, aby ująć treść pętli w nawiasy klamrowe, nawet w przypadku pojedynczej instrukcji. Nawiasy klamrowe sprawiają, że zakres jest jawny. Zapobiega to częstemu błędowi: dodaniu później drugiej instrukcji, po której oczekujesz, że będzie wykonywana przy każdej iteracji, a zamiast tego zostanie wykonana tylko raz po zakończeniu pętli. Tylko nawiasy klamrowe decydują, które instrukcje należą do pętli. Język C# nie traktuje białych znaków jako istotnych, więc samo wcięcie również nigdy nie ma znaczenia. Nawiasy klamrowe są legalne nawet wokół jednego wiersza: blok jest pojedynczą instrukcją powtarzaną przez pętlę. Stosuj wcięcia w kodzie dla czytelności, ale do definiowania bloku używaj nawiasów klamrowych.

Powtarzaj, dopóki warunek jest spełniony za pomocą polecenia while

Pętla while sprawdza warunek logiczny przed każdą iteracją. Jeśli na początku warunek ma wartość false, ciało pętli nigdy się nie wykona, więc pętla while wykona się zero lub więcej razy:

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--;
}

Upewnij się, że coś wewnątrz pętli zmienia warunek. W poprzednim przykładzie element countdown-- ostatecznie sprawia, że warunek przyjmuje wartość false. Pętla, której warunek nigdy nie przyjmuje wartości false, działa w nieskończoność.

Uruchom ciało co najmniej raz z użyciem do-while

Pętla do-while sprawdza swój stan po każdej iteracji, więc treść zawsze jest uruchamiana co najmniej raz. Użyj tego, gdy pierwsze wykonanie musi nastąpić, zanim będzie można ocenić warunek, na przykład aby pobrać dane wejściowe, a następnie sprawdzić ich poprawność:

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);

Licz za pomocą for

for Instrukcja pętli zawiera trzy części: inicjator uruchamiany raz przed pętlą, warunek sprawdzany przed każdą iterację i iterator uruchamiany po każdej iteracji. Użyj for, gdy potrzebujesz samego indeksu, a nie tylko elementów. Zazwyczaj indeks jest potrzebny, gdy chcesz zmodyfikować element, zamiast odczytywać jego wartość.

// 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
}

Gdy odczytujesz tylko elementy, gdy nie używasz pozycji lub przypiszesz nowe wartości, preferuj wartość foreach. Określa intencję bardziej wyraźnie i unika arytmetyki indeksu.

Wyjdź lub pomiń za pomocą break i continue

Dwie instrukcje skoku zapewniają dokładniejszą kontrolę w każdej pętli. Instrukcja break natychmiast zamyka pętlę, pomijając pozostałe iteracji:

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;
    }
}

Instrukcja continue pomija pozostałą część bieżącej iteracji i przechodzi do następnej:

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
}

Iterowanie strumienia asynchronicznego za pomocą polecenia await foreach

Strumień asynchroniczny to mechanizm odczytu, który wykorzystuje zadanie asynchroniczne do generowania każdego kolejnego elementu. Język C# reprezentuje go za pomocą interfejsu IAsyncEnumerable<T> . Dane, które docierają wraz z upływem czasu, takie jak strony z internetowego interfejsu API lub wierszy z bazy danych, pasują do tego modelu: pobieranie następnego elementu jest operacją oczekiwaną zamiast natychmiastowego powrotu.

Aby korzystać z asynchronicznego strumienia, umieść słowo kluczowe await przed foreach. Każda iteracja oczekuje na następny element, więc pętla zawiesza się podczas tworzenia tego elementu zamiast blokowania wątku:

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;
    }
}

Strumienie asynchroniczne bazują na async i await. Aby zapoznać się ze szczegółowym omówieniem, zobacz Generowanie i korzystanie ze strumieni asynchronicznych.

Zobacz także