Menu

Pętla for w C#: składnia, odliczanie w dół, pętle zagnieżdżone i pułapki

Jak działa pętla for w C#: inicjalizator, warunek i iterator, odliczanie w dół i krok, kilka zmiennych pętli, przechodzenie po tablicy przez indeks, pętle zagnieżdżone, for(;;) oraz błędy o jeden i usuwanie elementów w trakcie iteracji.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Pętla for powtarza blok, dopóki warunek jest prawdziwy, a przygotowanie i krok zapisuje się w nagłówku pętli. To pętla do liczenia: stała liczba powtórzeń albo każdy indeks tablicy.

Wynik:

Lap 1 of 5
Lap 2 of 5
Lap 3 of 5
Lap 4 of 5
Lap 5 of 5

Jak wykonują się trzy części

for (initializer; condition; iterator) wykonuje się w stałej kolejności:

  1. Inicjalizator (int i = 1) wykonuje się raz, przed wszystkim innym.
  2. Sprawdzany jest warunek (i <= 5). Jeśli jest fałszywy, pętla się kończy i ciało już się nie wykona.
  3. Wykonuje się ciało.
  4. Wykonuje się iterator (i++), po czym sterowanie wraca do kroku 2.

Ponieważ warunek jest sprawdzany przed każdą iteracją, pętla for może wykonać się zero razy: for (int i = 10; i < 5; i++) nigdy nie wchodzi do ciała. Powyższa pętla działa jak ta pętla while:

int i = 1;
while (i <= 5)
{
    Console.WriteLine($"Lap {i} of 5");
    i++;
}

Obie formy różnią się jednym szczegółem: continue w ciele for nadal uruchamia iterator, a w tej wersji z while przeskoczyłoby i++, więc pętla sprawdzałaby to samo i ponownie i mogłaby kręcić się bez końca. Zmienna zadeklarowana w inicjalizatorze istnieje tylko wewnątrz pętli. Jeśli potrzebujesz jej końcowej wartości po pętli, zadeklaruj ją przed pętlą: int i; for (i = 0; ...; i++) { }.

Odliczanie w dół i krok

Iterator może być dowolnym wyrażeniem. Zmniejszaj, aby odliczać w dół, dodawaj więcej niż jeden, aby iść krokiem, albo mnóż, aby uzyskać ciąg geometryczny:

Wynik:

3... 2... 1... liftoff
 00 15 30 45
 1 10 100 1000

Upewnij się, że iterator przybliża warunek do fałszu. for (int i = 0; i < 10; i--) oddala się od 10 i zatrzymuje się dopiero wtedy, gdy i przekręci się po dwóch miliardach iteracji.

Przechodzenie po tablicy przez indeks

Tablice mają Length, listy mają Count, a poprawne indeksy biegną od 0 do wartości o jeden mniejszej. Indeks daje coś, czego nie daje foreach: dostęp do pozycji, do sąsiadów i możliwość zapisu z powrotem.

Wynik:

#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

Ostatnia pętla zaczyna od 1, bo porównuje każdy element z poprzednim. Tworzenie, sortowanie i kopiowanie samych tablic omawia strona o tablicach.

Kilka zmiennych pętli

Inicjalizator może zadeklarować kilka zmiennych tego samego typu, a iterator może aktualizować kilka naraz, oddzielonych przecinkami. Klasyczne zastosowanie to dwa indeksy idące ku sobie:

Wynik:

racecar is a palindrome: True
9, 1, 8, 3

W jednej deklaracji obie zmienne muszą mieć ten sam typ: int left = 0, right = 10 działa, int i = 0, string s = "" nie.

Pętle zagnieżdżone

Pętla w pętli przechodzi cały swój zakres raz na każdą iterację pętli zewnętrznej. Typowy przypadek to tabele i siatki:

Wynik:

   1   2   3   4
   2   4   6   8
   3   6   9  12
   4   8  12  16

Wewnętrzne ciało wykonuje się 16 razy (4 × 4). Zagnieżdżanie szybko mnoży pracę: dwie zagnieżdżone pętle po 10 000 elementów każda to 100 milionów iteracji. break w pętli wewnętrznej opuszcza tylko pętlę wewnętrzną; strona o break i continue pokazuje, jak wyjść z obu.

Pętle nieskończone: for(;;)

Wszystkie trzy części nagłówka są opcjonalne. Bez warunku pętla działa, dopóki coś w jej wnętrzu jej nie opuści:

Wynik:

Month 1: 70 left
Month 2: 40 left
Month 3: 10 left
Overdrawn in month 4: -20

for (;;) i while (true) kompilują się do tego samego; wybierz to, czego używa twój projekt.

Błędy o jeden

Najczęstszy błąd w for to warunek, który idzie o krok za daleko:

Wynik:

Ada
Grace
Linus
IndexOutOfRangeException at index 3

Tablica 3 elementów ma indeksy 0, 1 i 2, więc właściwy warunek to i < names.Length. Lustrzany błąd to odliczanie w dół z i > 0, które nigdy nie odwiedza indeksu 0. W List<T> ten sam błąd rzuca zamiast tego ArgumentOutOfRangeException.

Usuwanie elementów w trakcie pętli

Usuwanie z List<T> w pętli idącej do przodu przesuwa każdy dalszy element o jedną pozycję w dół, więc pętla pomija element tuż za każdym usuniętym:

Wynik:

forward:  5, 0, 3, 7
backward: 5, 3, 7

Pętla do przodu zostawiła jedno 0: po usunięciu indeksu 1 drugie zero przesunęło się na indeks 1, ale i++ przeszło już do indeksu 2. Pętla od tyłu tego unika, bo usunięcia przesuwają tylko elementy już odwiedzone. Alternatywa w jednej linii, stock.RemoveAll(x => x == 0), robi to samo. Pętla foreach w ogóle nie pozwala na usuwanie; rzuca InvalidOperationException.

Liczniki zmiennoprzecinkowe

Unikaj liczników pętli typu double sprawdzanych przez równość. 0.1 nie ma dokładnej reprezentacji binarnej, więc dziesięciokrotne dodanie tej wartości nie daje dokładnie 1.0:

for (double x = 0; x != 1.0; x += 0.1)   // never ends: x skips past 1.0
{
}

Zamiast tego licz liczbą całkowitą i obliczaj z niej wartość: for (int i = 0; i <= 10; i++) { double x = i / 10.0; }.

for a foreach

PotrzebaUżyj
Odczytać każdy element po koleiforeach
Indeks albo sąsiedzi (i - 1, i + 1)for
Przypisać wartość do elementu tablicyfor
Iść krokiem 2, iść od tyłufor
Usuwać z List w trakcie pętlifor, od tyłu
Kolekcja bez indeksera (HashSet, Dictionary, wyniki LINQ)foreach
Powtórzyć coś N razy, bez kolekcjifor

Dla tablic obie pętle kompilują się do praktycznie tego samego kodu, więc wybieraj według czytelności, a nie szybkości.

Najczęściej zadawane pytania

Jaka jest składnia pętli for w C#?

for (initializer; condition; iterator) { body }, na przykład for (int i = 0; i < 5; i++) { ... }. Inicjalizator wykonuje się raz, warunek jest sprawdzany przed każdą iteracją, a iterator wykonuje się po każdej iteracji. Pętla kończy się, gdy warunek po raz pierwszy okaże się fałszywy.

Jak w C# przejść pętlą od tyłu?

Zacznij od ostatniego indeksu i zmniejszaj go: for (int i = items.Length - 1; i >= 0; i--). Zwróć uwagę na >= 0, a nie > 0, inaczej pierwszy element zostanie pominięty. Pętla od tyłu to też bezpieczny sposób usuwania elementów z List w trakcie iteracji.

Kiedy w C# użyć for zamiast foreach?

Użyj for, gdy potrzebujesz indeksu (żeby porównać sąsiadów, zapisać coś z powrotem do tablicy albo iść krokiem większym niż jeden), gdy liczysz zamiast przechodzić kolekcję albo gdy usuwasz elementy z listy. Użyj foreach, aby odczytać wszystkie elementy po kolei; jest krótsza i nie ma indeksu, w którym można się pomylić.

Jak napisać nieskończoną pętlę w C#?

for (;;) { ... } albo while (true) { ... }. Wszystkie trzy części nagłówka for są opcjonalne, a brak warunku oznacza prawdę. Wyjdź z pętli przez break albo return, gdy spełni się warunek wyjścia.

Dlaczego moja pętla for rzuca IndexOutOfRangeException?

Warunek pozwala indeksowi dojść do długości: i <= array.Length odwiedza array[array.Length], czyli pozycję za ostatnim elementem. Poprawne indeksy biegną od 0 do Length - 1, więc warunek powinien brzmieć i < array.Length (albo i < list.Count dla List, która zamiast tego rzuca ArgumentOutOfRangeException).

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ