Iterationsanweisungen: for, foreach, do und while

Die folgenden Iterationsanweisungen führen eine Anweisung bzw. einen Anweisungsblock wiederholt aus. Die for-Anweisung führt ihren Körper aus, während ein angegebener boolescher Ausdruck als true ausgewertet wird. Die foreach-Anweisung zählt die Elemente einer Auflistung auf und führt ihren Körper für jedes Element der Auflistung aus. Die do-Anweisung führt ihren Körper unter bestimmten Bedingungen einmal oder mehrmals aus. Die while-Anweisung führt ihren Körper unter bestimmten Bedingungen keinmal oder mehrmals aus.

Sie können die Schleife zu jedem Zeitpunkt in einer Iterationsanweisung mit der break-Anweisung verlassen. Sie können mit der continue-Anweisung zur nächsten Iteration in der Schleife wechseln.

Die Anweisung for

Die Anweisung for führt eine Anweisung oder einen Anweisungsblock aus, während ein angegebener boolescher Ausdruck true ergibt. Das folgende Beispiel zeigt die Anweisung for, die ihren Rumpf ausführt, solange ein Zähler für eine ganze Zahl kleiner als drei ist:

for (int i = 0; i < 3; i++)
{
    Console.Write(i);
}
// Output:
// 012

Das vorherige Beispiel zeigt die Elemente der for-Anweisung:

  • Den Abschnitt initializer, der nur einmal ausgeführt wird, bevor die Schleife beginnt. In der Regel deklarieren und initialisieren Sie in diesem Abschnitt eine lokale Schleifenvariable. Auf die deklarierte Variable kann von außerhalb der for-Anweisung nicht zugegriffen werden.

    Der Abschnitt initializer im vorherigen Beispiel deklariert und initialisiert eine Variable für einen Zähler mit ganzer Zahl:

    int i = 0
    
  • Den Abschnitt Bedingung, der bestimmt, ob die nächste Iteration in der Schleife ausgeführt werden soll. Wenn die Auswertung true ergibt oder fehlt, wird die nächste Iteration ausgeführt, andernfalls wird die Schleife verlassen. Der Abschnitt condition muss ein boolescher Ausdruck sein.

    Der Abschnitt Bedingung im vorherigen Beispiel prüft, ob ein Zählerwert kleiner als drei ist:

    i < 3
    
  • Den Abschnitt iterator, der definiert, was nach jeder Iteration des Schleifenrumpfs geschieht.

    Der Abschnitt iterator im vorhergehenden Beispiel erhöht den Zähler:

    i++
    
  • Den Schleifenrumpf, der entweder eine Anweisung oder ein Anweisungsblock sein muss.

Der Abschnitt „iterator“ enthält keine oder mehrere der folgenden durch Komma getrennten Anweisungsausdrücke:

Wenn Sie im Abschnitt „initializer“ keine Schleifenvariable deklarieren, können Sie keinen oder mehrere der Ausdrücke in der vorhergehenden Liste auch im Abschnitt „initializer“ verwenden. Das folgende Beispiel veranschaulicht mehrere weniger übliche Verwendungen der Abschnitte „initializer“ und „iterator“: das Zuweisen eines Werts für eine externe Schleifenvariable im Abschnitt „initializer“, das Aufrufen einer Methode in den Abschnitten „initializer“ und „iterator“ und das Ändern der Werte zweier Variablen im Abschnitt „iterator“.

int i;
int j = 3;
for (i = 0, Console.WriteLine($"Start: i={i}, j={j}"); i < j; i++, j--, Console.WriteLine($"Step: i={i}, j={j}"))
{
    //...
}
// Output:
// Start: i=0, j=3
// Step: i=1, j=2
// Step: i=2, j=1

Alle Abschnitte der for-Anweisung sind optional. Im folgenden Beispiel wird die Endlosschleife for definiert:

for ( ; ; )
{
    //...
}

Die Anweisung foreach

Die Anweisung foreach führt eine Anweisung oder einen Block von Anweisungen für jedes Element in einer Instanz des Typs aus, der die Schnittstellen System.Collections.IEnumerable oder System.Collections.Generic.IEnumerable<T> implementiert. Dies wird im folgenden Beispiel gezeigt:

List<int> fibNumbers = [0, 1, 1, 2, 3, 5, 8, 13];
foreach (int element in fibNumbers)
{
    Console.Write($"{element} ");
}
// Output:
// 0 1 1 2 3 5 8 13

Die Anweisung foreach ist nicht auf diese Typen beschränkt. Sie können sie mit einer Instanz eines beliebigen Typs verwenden, der die folgenden Bedingungen erfüllt:

  • Ein Typ hat die öffentliche parameterlose GetEnumerator-Methode. Diese GetEnumerator-Methode kann die Lieblings Erweiterungsmethode eines Typs sein.
  • Der Rückgabetyp der Methode GetEnumerator weist die öffentliche Eigenschaft Current und die öffentliche parameterlose Methode MoveNext auf, deren Rückgabetyp bool ist.

Im folgenden Beispiel wird die Anweisung foreach mit einer Instanz des Typs System.Span<T> verwendet, der keine Schnittstellen implementiert:

Span<int> numbers = [3, 14, 15, 92, 6];
foreach (int number in numbers)
{
    Console.Write($"{number} ");
}
// Output:
// 3 14 15 92 6

Sie können eine Iterationsvariable mit den Modifizierern ref oder ref readonly deklarieren, wenn die Eigenschaft Current des Enumerators einen Verweisrückgabewert (ref T, wobei T dem Typ des Sammlungselements entspricht) zurückgibt. Dies wird im folgenden Beispiel gezeigt:

Span<int> storage = stackalloc int[10];
int num = 0;
foreach (ref int item in storage)
{
    item = num++;
}
foreach (ref readonly var item in storage)
{
    Console.Write($"{item} ");
}
// Output:
// 0 1 2 3 4 5 6 7 8 9

Falls die Quellsammlung der foreach-Anweisung leer ist, wird der Rumpf der foreach-Anweisung nicht ausgeführt und übersprungen. Wenn die foreach-Anweisung auf null angewendet wird, wird NullReferenceException ausgelöst.

await foreach

Sie können die Anweisung await foreach verwenden, um einen asynchronen Datenstrom zu nutzen, also den Sammlungstyp, der die Schnittstelle IAsyncEnumerable<T>-implementiert. Jede Iteration der Schleife kann unterbrochen werden, während das nächste Element asynchron abgerufen wird. Im folgenden Beispiel wird veranschaulicht, wie Sie die Anweisung await foreach verwenden:

await foreach (var item in GenerateSequenceAsync())
{
    Console.WriteLine(item);
}

Sie können die await foreach-Anweisung auch mit einer Instanz eines beliebigen Typs verwenden, der die folgenden Bedingungen erfüllt:

  • Ein Typ hat die öffentliche parameterlose GetAsyncEnumerator-Methode. Diese Methode kann die Erweiterungsmethode eines Typs sein.
  • Der Rückgabetyp der GetAsyncEnumerator-Methode hat die öffentliche Current-Eigenschaft und die öffentliche parameterlose MoveNextAsync-Methode, deren Rückgabetyp Task<bool>, ValueTask<bool> oder ein beliebiger anderer awaitable-Typ ist, dessen GetResult-Methode des „awaiter“-Elements einen bool-Wert zurückgibt.

Standardmäßig werden Streamelemente im erfassten Kontext verarbeitet. Wenn Sie die Erfassung des Kontexts deaktivieren möchten, verwenden Sie die Erweiterungsmethode TaskAsyncEnumerableExtensions.ConfigureAwait. Weitere Informationen über Synchronisierungskontexte und die Erfassung des aktuellen Kontexts finden Sie im Artikel Verwenden des aufgabenbasierten asynchronen Musters. Weitere Informationen zu asynchronen Datenströmen finden Sie im Abschnitt Tutorial zu asynchronen Datenströmen.

Typ einer Iterationsvariablen

Sie können das Schlüsselwort var verwenden, damit der Compiler den Typ einer Iterationsvariablen in der foreach-Anweisung ableiten kann. Dies wird im folgenden Code gezeigt:

foreach (var item in collection) { }

Hinweis

Der Typ von var kann vom Compiler als singbarer Bezugstyp abgeleitet werden, je nachdem, ob der mit Nullwerte kompatiblen Kontext aktiviert ist und ob der Typ eines Initialisierungsausdrucks ein Verweistyp ist. Weitere Informationen zu finden Sie unter Implizit typisierte lokale Variablen.

Sie können auch wie im folgenden Code explizit den Typ einer Iterationsvariablen angeben:

IEnumerable<T> collection = new T[5];
foreach (V item in collection) { }

Im obigen Formular muss der Typ T eines Sammlungselements implizit oder explizit in Typ V einer Iterationsvariablen konvertierbar sein. Wenn eine explizite Konvertierung von T in V zur Laufzeit fehlschlägt, löst die Anweisung foreach eine InvalidCastException aus. Wenn T z. B. ein nicht versiegelter Klassentyp ist, kann V ein beliebiger Schnittstellentyp sein – sogar der Typ, den T nicht implementiert. Zur Laufzeit kann der Typ eines Sammlungselements der Typ sein, der von T abgeleitet wird und V implementiert. Wenn dies nicht der Fall ist, wird eine InvalidCastException ausgelöst.

Die Anweisung do

Die Anweisung do führt eine Anweisung oder einen Anweisungsblock aus, während ein angegebener boolescher Ausdruck true ergibt. Da der Ausdruck nach jeder Ausführung der Schleife ausgewertet wird, wird eine do-Schleife mindestens einmal ausgeführt. Die do-Schleife unterscheidet sich von der while-Schleife, die nie oder mehrmals ausgeführt wird.

Im folgenden Beispiel wird die Verwendung der do-Anweisung veranschaulicht:

int n = 0;
do
{
    Console.Write(n);
    n++;
} while (n < 5);
// Output:
// 01234

Die Anweisung while

Die Anweisung while führt eine Anweisung oder einen Anweisungsblock aus, während ein angegebener boolescher Ausdruck true ergibt. Da der Ausdruck vor jeder Ausführung der Schleife ausgewertet wird, wird eine while-Schleife entweder nie oder mehrmals ausgeführt. Die while-Schleife unterscheidet sich von der do-Schleife, die einmal oder mehrmals ausgeführt wird.

Im folgenden Beispiel wird die Verwendung der while-Anweisung veranschaulicht:

int n = 0;
while (n < 5)
{
    Console.Write(n);
    n++;
}
// Output:
// 01234

C#-Sprachspezifikation

Weitere Informationen finden Sie in den folgenden Abschnitten der C#-Sprachspezifikation:

Weitere Informationen zu diesen Features finden Sie in den folgenden Featurevorschlägen:

Siehe auch