Eine for-Schleife wiederholt einen Block, solange eine Bedingung wahr ist, wobei Vorbereitung und Schritt im Schleifenkopf stehen. Sie ist die Schleife fürs Zählen: eine feste Anzahl an Wiederholungen oder jeder Index eines Arrays.
Ausgabe:
Lap 1 of 5
Lap 2 of 5
Lap 3 of 5
Lap 4 of 5
Lap 5 of 5
Wie die drei Teile ablaufen
for (initializer; condition; iterator) läuft in einer festen Reihenfolge ab:
- Der Initialisierer (
int i = 1) läuft einmal, vor allem anderen. - Die Bedingung (
i <= 5) wird geprüft. Ist sie false, endet die Schleife, und der Rumpf läuft nie wieder. - Der Rumpf läuft.
- Der Iterator (
i++) läuft, dann geht es zurück zu Schritt 2.
Weil die Bedingung vor jedem Durchlauf geprüft wird, kann eine for-Schleife null Mal laufen: for (int i = 10; i < 5; i++) betritt ihren Rumpf nie. Die Schleife oben verhält sich wie diese while-Schleife:
int i = 1;
while (i <= 5)
{
Console.WriteLine($"Lap {i} of 5");
i++;
}
Die beiden Formen unterscheiden sich in einem Detail: continue in einem for-Rumpf führt den Iterator trotzdem aus, während es in dieser while-Version i++ überspringen würde. Die Schleife würde dann dasselbe i erneut prüfen und könnte endlos laufen. Eine im Initialisierer deklarierte Variable existiert nur innerhalb der Schleife. Wenn du danach ihren Endwert brauchst, deklariere sie vor der Schleife: int i; for (i = 0; ...; i++) { }.
Rückwärts zählen und Schrittweiten
Der Iterator kann ein beliebiger Ausdruck sein. Dekrementiere, um rückwärts zu zählen, addiere mehr als eins für größere Schritte oder multipliziere für eine geometrische Folge:
Ausgabe:
3... 2... 1... liftoff
00 15 30 45
1 10 100 1000
Achte darauf, dass der Iterator dafür sorgt, dass die Bedingung irgendwann false wird. for (int i = 0; i < 10; i--) zählt von 10 weg und hört erst auf, wenn i nach zwei Milliarden Durchläufen umspringt.
Ein Array per Index durchlaufen
Arrays haben Length, Listen haben Count, und gültige Indizes reichen von 0 bis eins weniger. Der Index gibt dir etwas, das foreach nicht bietet: Zugriff auf die Position, die Nachbarn und das Recht, zurückzuschreiben.
Ausgabe:
#1: 17.99
#2: 4.50
#3: 38.25
#4: 7.43
item 1 vs item 0: down
item 2 vs item 1: up
item 3 vs item 2: down
Die letzte Schleife beginnt bei 1, weil sie jedes Element mit dem vorherigen vergleicht. Die Seite zu Arrays behandelt, wie man Arrays selbst erzeugt, sortiert und kopiert.
Mehrere Schleifenvariablen
Der Initialisierer kann mehrere Variablen desselben Typs deklarieren, und der Iterator kann mehrere aktualisieren, getrennt durch Kommas. Der klassische Einsatz sind zwei Indizes, die aufeinander zulaufen:
Ausgabe:
racecar is a palindrome: True
9, 1, 8, 3
Beide Variablen müssen in einer einzelnen Deklaration denselben Typ haben: int left = 0, right = 10 funktioniert, int i = 0, string s = "" nicht.
Verschachtelte Schleifen
Eine Schleife in einer Schleife durchläuft ihren ganzen Bereich einmal pro Durchlauf der äußeren Schleife. Tabellen und Raster sind der typische Fall:
Ausgabe:
1 2 3 4
2 4 6 8
3 6 9 12
4 8 12 16
Der innere Rumpf läuft 16 Mal (4 × 4). Verschachtelung vervielfacht die Arbeit schnell: Zwei verschachtelte Schleifen über je 10.000 Elemente ergeben 100 Millionen Durchläufe. Ein break in der inneren Schleife verlässt nur die innere Schleife; die Seite zu break und continue zeigt, wie man beide verlässt.
Endlosschleifen: for(;;)
Alle drei Teile des Kopfs sind optional. Ohne Bedingung läuft die Schleife, bis etwas in ihr sie verlässt:
Ausgabe:
Month 1: 70 left
Month 2: 40 left
Month 3: 10 left
Overdrawn in month 4: -20
for (;;) und while (true) kompilieren zum selben Ergebnis; nimm, was in deiner Codebasis üblich ist.
Off-by-one-Fehler
Der häufigste for-Bug ist eine Bedingung, die einen Schritt zu weit geht:
Ausgabe:
Ada
Grace
Linus
IndexOutOfRangeException at index 3
Ein Array mit 3 Elementen hat die Indizes 0, 1 und 2, i < names.Length ist also die richtige Bedingung. Der spiegelbildliche Bug ist das Rückwärtszählen mit i > 0, das Index 0 nie besucht. Bei einer List<T> wirft derselbe Fehler stattdessen ArgumentOutOfRangeException.
Elemente beim Durchlaufen entfernen
Wer in einer vorwärts laufenden Schleife aus einer List<T> entfernt, verschiebt jedes spätere Element um eine Position nach vorn, die Schleife überspringt also das Element direkt nach jeder Entfernung:
Ausgabe:
forward: 5, 0, 3, 7
backward: 5, 3, 7
Die vorwärts laufende Schleife hat eine 0 übrig gelassen: Nach dem Entfernen von Index 1 ist die zweite Null auf Index 1 gerückt, aber i++ war schon zu Index 2 weitergegangen. Rückwärts zu laufen vermeidet das, weil Entfernungen nur Elemente verschieben, die du schon besucht hast. Als einzeilige Alternative tut stock.RemoveAll(x => x == 0) dasselbe. Eine foreach-Schleife erlaubt das Entfernen überhaupt nicht; sie wirft InvalidOperationException.
Gleitkommazähler
Vermeide double-Schleifenzähler, die auf Gleichheit prüfen. 0.1 hat keine exakte binäre Darstellung, zehnmaliges Addieren ergibt also nicht genau 1.0:
for (double x = 0; x != 1.0; x += 0.1) // never ends: x skips past 1.0
{
}
Zähle stattdessen mit einer Ganzzahl und berechne den Wert daraus: for (int i = 0; i <= 10; i++) { double x = i / 10.0; }.
for gegenüber foreach
| Bedarf | Verwende |
|---|---|
| Jedes Element der Reihe nach lesen | foreach |
Den Index oder die Nachbarn (i - 1, i + 1) | for |
| In ein Array-Element schreiben | for |
| In Zweierschritten gehen, rückwärts gehen | for |
Beim Durchlaufen aus einer List entfernen | for, rückwärts |
Eine Collection ohne Indexer (HashSet, Dictionary, LINQ-Ergebnisse) | foreach |
| Etwas N Mal wiederholen, ohne Collection | for |
Bei Arrays kompilieren beide Schleifen zu im Wesentlichen demselben Code, wähle also nach Lesbarkeit, nicht nach Geschwindigkeit.
Häufig gestellte Fragen
Wie lautet die Syntax einer for-Schleife in C#?
for (initializer; condition; iterator) { body }, zum Beispiel for (int i = 0; i < 5; i++) { ... }. Der Initialisierer läuft einmal, die Bedingung wird vor jedem Durchlauf geprüft, und der Iterator läuft nach jedem Durchlauf. Die Schleife endet, sobald die Bedingung zum ersten Mal false ist.
Wie durchlaufe ich in C# eine Schleife rückwärts?
Beginne beim letzten Index und dekrementiere: for (int i = items.Length - 1; i >= 0; i--). Beachte >= 0, nicht > 0, sonst wird das erste Element übersprungen. Rückwärts zu laufen ist auch der sichere Weg, beim Durchlaufen Elemente aus einer List zu entfernen.
Wann sollte ich in C# for statt foreach verwenden?
Nimm for, wenn du den Index brauchst (um Nachbarn zu vergleichen, in ein Array zurückzuschreiben oder in Schritten größer als eins zu gehen), wenn du zählst, statt eine Collection zu durchlaufen, oder wenn du Elemente aus einer Liste entfernst. Nimm foreach, um jedes Element der Reihe nach zu lesen; das ist kürzer und hat keinen Index, bei dem man sich vertun kann.
Wie schreibe ich in C# eine Endlosschleife?
for (;;) { ... } oder while (true) { ... }. Alle drei Teile eines for-Kopfs sind optional, und eine fehlende Bedingung gilt als true. Verlasse die Schleife mit break oder return, sobald deine Abbruchbedingung erfüllt ist.
Warum bekomme ich in meiner for-Schleife eine IndexOutOfRangeException?
Die Bedingung lässt den Index die Länge erreichen: i <= array.Length besucht array[array.Length], eine Position hinter dem letzten Element. Gültige Indizes reichen von 0 bis Length - 1, die Bedingung sollte also i < array.Length lauten (oder i < list.Count bei einer List, die stattdessen ArgumentOutOfRangeException wirft).