Przejdź do treści
School IT / Przestrzeń do nauki
← C# od podstawLekcja 14 z 58

Instrukcja warunkowa if / else

Do tej pory twoje programy wykonywały wszystkie linie po kolei, od pierwszej do ostatniej — zawsze tak samo. Od tej lekcji program zaczyna podejmować decyzje: jedne linie wykona, inne pominie, w zależności od tego, jakie dane dostał.

C# if / else operator ?: 45 min
CEL LEKCJI

Czego się dziś nauczysz

  • Wykonasz fragment kodu tylko wtedy, gdy warunek jest spełniony
  • Zbudujesz drabinkę else if i ustawisz jej warunki we właściwej kolejności
  • Wyjaśnisz, dlaczego w C# nie da się napisać if (x = 5)
  • Skrócisz proste rozgałęzienie operatorem ?:
  • Zrozumiesz, dlaczego zmienna zadeklarowana w bloku znika po jego zamknięciu
TEORIA

Instrukcja if

Instrukcja if mówi: „jeśli warunek jest prawdziwy, wykonaj to, co w klamrach; jeśli nie — pomiń”.

składnia
if (warunek)
{
    // instrukcje wykonywane, gdy warunek jest prawdziwy
}
Program.cs
int wiek = 20;

Console.WriteLine("Sprawdzam wiek...");

if (wiek >= 18)
{
    Console.WriteLine("Jestes pelnoletni.");
}

Console.WriteLine("Koniec sprawdzania.");
wynik dla wiek = 20
Sprawdzam wiek...
Jestes pelnoletni.
Koniec sprawdzania.
wynik dla wiek = 15
Sprawdzam wiek...
Koniec sprawdzania.

Zwróć uwagę, że ostatnia linia wykonała się w obu przypadkach — jest poza klamrami, więc if jej nie dotyczy.

Po nawiasie warunku nie ma średnika

Zapis if (wiek >= 18); to pusta instrukcja: „jeśli pełnoletni, nie rób nic”. Blok w klamrach wykona się wtedy zawsze. Kompilator ostrzeże (CS0642), ale programu nie zatrzyma, więc łatwo to przeoczyć.

Klamry przy jednej instrukcji

Gdy w środku jest tylko jedna instrukcja, klamry można pominąć. Ale nie warto. Porównaj:

Program.cs
// dziala, ale kruche
if (saldo < 0)
    Console.WriteLine("Debet!");

// ktos dopisuje druga linie i program przestaje dzialac poprawnie
if (saldo < 0)
    Console.WriteLine("Debet!");
    Console.WriteLine("Zablokowano karte.");   // wykona sie ZAWSZE

Wcięcie sugeruje, że obie linie należą do if — ale kompilator wcięć nie czyta. Bez klamer do if należy dokładnie jedna instrukcja, ta pierwsza. W tym kursie klamry stawiamy zawsze.

TEORIA

Warunek musi być typu bool

W nawiasie if może stać wyłącznie wyrażenie typu bool — czyli takie, które daje true albo false. Nie liczba, nie napis. To jedna z ważniejszych różnic między C# a językami C i C++.

Program.cs
int x = 5;

if (x)          // BLAD CS0029: nie mozna zamienic int na bool
{
}

if (x = 5)      // BLAD CS0029: to przypisanie, wynikiem jest liczba 5
{
}

if (x == 5)     // OK: porownanie, wynikiem jest true
{
}

Kompilator C# ratuje cię przed klasycznym błędem

W języku C zapis if (x = 5) jest poprawny — przypisuje 5 i uznaje wynik za prawdę, więc warunek zawsze jest spełniony. To źródło setek tysięcy błędów. W C# taki zapis po prostu się nie skompiluje, bo liczba nie jest wartością logiczną. Jeden znak = zamiast dwóch zostanie natychmiast wychwycony.

Zmienna logiczna jako warunek

Skoro warunek to bool, to zmienną typu bool można wstawić wprost — bez porównywania z true:

Program.cs
bool ok = int.TryParse("42", out int liczba);

if (ok)             // dobrze
{
    Console.WriteLine(liczba);
}

if (ok == true)     // dziala, ale to maslo maslane
{
}

if (!ok)            // zaprzeczenie: "jesli sie NIE udalo"
{
    Console.WriteLine("Blad danych.");
}

Nazwa zmiennej logicznej powinna brzmieć jak zdanie oznajmujące, żeby warunek dało się przeczytać po polsku: if (czyPelnoletni), if (znaleziono), if (!pusty).

TEORIA

else i else if

else to „w przeciwnym razie”. Blok else wykona się dokładnie wtedy, gdy warunek if był fałszywy — nie trzeba pisać warunku odwrotnego.

Program.cs
int wiek = 15;

if (wiek >= 18)
{
    Console.WriteLine("Pelnoletni");
}
else
{
    Console.WriteLine("Niepelnoletni");
}

Zawsze wykona się dokładnie jedna z dwóch gałęzi — nigdy obie i nigdy żadna.

Więcej niż dwie możliwości: else if

Program.cs
int temperatura = 12;

if (temperatura < 0)
{
    Console.WriteLine("Mroz");
}
else if (temperatura < 10)
{
    Console.WriteLine("Zimno");
}
else if (temperatura < 20)
{
    Console.WriteLine("Chlodno");
}
else
{
    Console.WriteLine("Cieplo");
}
wynik dla temperatura = 12
Chlodno

Drabinka kończy się na pierwszym trafieniu

Program sprawdza warunki po kolei od góry i zatrzymuje się na pierwszym prawdziwym. Dla 12 stopni: 12 < 0 to fałsz, 12 < 10 to fałsz, 12 < 20 to prawda → wypisuje „Chlodno” i nie sprawdza już reszty. Dlatego nie trzeba pisać else if (temperatura >= 10 && temperatura < 20) — wiadomo już, że poprzednie warunki były fałszywe.

Ostatni else jest opcjonalny, ale warto go mieć: łapie wszystkie przypadki, o których nie pomyślałeś. Bez niego, gdy żaden warunek nie pasuje, program po prostu nic nie zrobi — i nie dowiesz się dlaczego.

TEORIA

Kolejność warunków ma znaczenie

To najczęstszy błąd logiczny początkujących — kod się kompiluje, uruchamia i daje złe wyniki. Spójrz na dwie wersje tego samego przelicznika procentów na ocenę:

źle
int procent = 95;

if (procent >= 50)      // 95 >= 50 to PRAWDA, drabinka sie konczy
{
    Console.WriteLine("3");
}
else if (procent >= 75)
{
    Console.WriteLine("4");
}
else if (procent >= 90)
{
    Console.WriteLine("5");
}
wynik dla procent = 95
3
dobrze
int procent = 95;

if (procent >= 90)
{
    Console.WriteLine("5");
}
else if (procent >= 75)
{
    Console.WriteLine("4");
}
else if (procent >= 50)
{
    Console.WriteLine("3");
}
else
{
    Console.WriteLine("2");
}
wynik dla procent = 95
5

Reguła: od najostrzejszego warunku do najłagodniejszego

Przy progach z >= zaczynamy od największej wartości i schodzimy w dół. Przy progach z <= — odwrotnie, od najmniejszej w górę. Jeśli warunki są rozłączne (np. porównania z różnymi napisami), kolejność nie ma znaczenia.

TEORIA

Zagnieżdżanie a &&

Wewnątrz if może stać kolejny if. Czasem to konieczne, ale bardzo często da się to zapisać prościej — operatorem && z lekcji 10:

Program.cs
// zagniezdzenie
if (wiek >= 18)
{
    if (maPrawoJazdy)
    {
        Console.WriteLine("Moze prowadzic.");
    }
}

// to samo, czytelniej
if (wiek >= 18 && maPrawoJazdy)
{
    Console.WriteLine("Moze prowadzic.");
}

Zagnieżdżenie jest potrzebne dopiero wtedy, gdy wewnętrzny warunek ma własne else:

Program.cs
if (wiek >= 18)
{
    if (maPrawoJazdy)
    {
        Console.WriteLine("Moze prowadzic.");
    }
    else
    {
        Console.WriteLine("Pelnoletni, ale bez uprawnien.");
    }
}
else
{
    Console.WriteLine("Za mlody.");
}

Trzy poziomy zagnieżdżenia to sygnał ostrzegawczy

Kod wcięty na cztery poziomy jest prawie nie do przeczytania. Zwykle da się go spłaszczyć: połączyć warunki przez &&, odwrócić warunek i wyjść wcześniej, albo — od bloku o funkcjach — wydzielić fragment do osobnej metody.

TEORIA

Zmienne zadeklarowane w bloku

Klamry wyznaczają zasięg. Zmienna zadeklarowana wewnątrz bloku przestaje istnieć po klamrze zamykającej:

Program.cs
if (procent >= 50)
{
    string wynik = "zdany";
}

Console.WriteLine(wynik);   // BLAD CS0103: nazwa 'wynik' nie istnieje

Poprawka: deklarujemy zmienną przed instrukcją warunkową, a w blokach tylko przypisujemy wartość.

Program.cs
string wynik;

if (procent >= 50)
{
    wynik = "zdany";
}
else
{
    wynik = "niezdany";
}

Console.WriteLine(wynik);   // OK

Każda ścieżka musi coś przypisać

Gdyby zabrakło gałęzi else, kompilator zgłosiłby CS0165: Use of unassigned local variable 'wynik'. To nie jest przesadna ostrożność — przy fałszywym warunku zmienna naprawdę nie miałaby żadnej wartości. Albo dopisz else, albo nadaj wartość początkową przy deklaracji: string wynik = "niezdany";.

TEORIA

Operator warunkowy ?:

Gdy całe rozgałęzienie sprowadza się do wybrania jednej z dwóch wartości, sześć linii z if/else można zastąpić jedną. Operator warunkowy (nazywany też trójargumentowym, bo jako jedyny w C# przyjmuje trzy argumenty) czyta się jak pytanie:

składnia
warunek ? wartosc_gdy_prawda : wartosc_gdy_falsz
Program.cs
int wiek = 15;

// wersja z if
string opis1;
if (wiek >= 18)
{
    opis1 = "pelnoletni";
}
else
{
    opis1 = "niepelnoletni";
}

// dokladnie to samo, jedna linia
string opis2 = wiek >= 18 ? "pelnoletni" : "niepelnoletni";

Console.WriteLine(opis2);

Operator warunkowy zwraca wartość, więc można go wstawić wszędzie tam, gdzie wartość jest potrzebna — także wprost w interpolacji:

Program.cs
int sztuki = 1;
double cena = 24.99;
double rabat = sztuki >= 10 ? 0.15 : 0.0;

Console.WriteLine($"Kupiles {sztuki} {(sztuki == 1 ? "sztuke" : "sztuk")}.");
Console.WriteLine($"Rabat: {rabat:P0}");
wynik w konsoli
Kupiles 1 sztuke.
Rabat: 0 %

Nawias wewnątrz interpolacji

W zapisie {(sztuki == 1 ? "sztuke" : "sztuk")} nawias jest obowiązkowy. Bez niego dwukropek zostałby uznany za początek specyfikatora formatu z poprzedniej lekcji i kompilator zgłosiłby błąd.

Nie zagnieżdżaj operatora warunkowego

Zapis a ? "x" : b ? "y" : "z" jest poprawny, ale nieczytelny — a przy trzech poziomach nie da się go już zrozumieć bez kartki. Do wyboru z wielu możliwości służy drabinka else if albo instrukcja switch z następnej lekcji.

Krewny: operator ??

Znasz go już z lekcji 12. To skrót dla jednego konkretnego pytania: „czy wartość istnieje?”.

Program.cs
string? wpis = Console.ReadLine();

string imie = wpis ?? "Nieznajomy";     // gdy nic nie ma, uzyj wartosci zapasowej

// to samo dluzej
string imie2 = wpis != null ? wpis : "Nieznajomy";
METODY WBUDOWANE

Metody przydatne w warunkach

NazwaZwracaDziałanie
string.IsNullOrEmpty(s)bool true, gdy napis nie istnieje albo ma zero znaków. Zastępuje zapis s == null || s.Length == 0.
string.IsNullOrWhiteSpace(s)bool To samo, ale za pusty uznaje też napis złożony z samych spacji i tabulatorów. Do sprawdzania danych od użytkownika lepszy niż poprzedni — ktoś, kto naciśnie spację i Enter, nie wpisał imienia.
s.StartsWith(t) / s.EndsWith(t)bool Czy napis zaczyna się / kończy podanym fragmentem. Rozróżnia wielkość liter, chyba że podasz StringComparison.OrdinalIgnoreCase.
s.Contains(t)bool Czy napis zawiera podany fragment gdziekolwiek w środku.
string.Equals(a, b, porownanie)bool Porównanie napisów z jawną regułą. StringComparison.OrdinalIgnoreCase ignoruje wielkość liter — właściwy sposób na sprawdzenie odpowiedzi „tak”/„TAK”/„Tak”.
char.IsDigit(c)bool Czy znak jest cyfrą. Rodzeństwo: char.IsLetter, char.IsUpper, char.IsWhiteSpace.
Math.Abs(x)liczba Wartość bezwzględna. Potrzebna przy porównywaniu liczb ułamkowych: Math.Abs(a - b) < 0.0001 zamiast a == b.
Math.Max(a, b) / Math.Min(a, b)liczba Większa / mniejsza z dwóch wartości. Często zastępuje całe if: int wiekszy = Math.Max(a, b);.
Math.Sqrt(x)double Pierwiastek kwadratowy. Dla liczby ujemnej zwraca NaN („nie liczba”) — nie zgłasza błędu, więc znak trzeba sprawdzić samemu.
Program.cs
Console.Write("Imie: ");
string imie = Console.ReadLine() ?? "";

if (string.IsNullOrWhiteSpace(imie))
{
    Console.WriteLine("Nie podales imienia.");
}
else
{
    Console.WriteLine($"Czesc, {imie}!");
}
PRZYKŁAD Z OMÓWIENIEM

Wynik szkolnego turnieju

Reguły ćwiczenia: 90–100 punktów daje finał, 50–89 dobry wynik, a 0–49 oznacza dalszy trening. Wartości spoza 0–100 są błędne.

Program.cs — cały przykład
Console.Write("Punkty w turnieju (0-100): ");
bool poprawnyZapis = int.TryParse(Console.ReadLine(), out int punkty);

if (!poprawnyZapis)
{
    Console.WriteLine("Wpisz liczbe calkowita.");
}
else if (punkty < 0 || punkty > 100)
{
    Console.WriteLine("Punkty musza byc w zakresie 0-100.");
}
else
{
    string kategoria;
    if (punkty >= 90) kategoria = "Finalista";
    else if (punkty >= 50) kategoria = "Dobry wynik";
    else kategoria = "Czas na trening";

    Console.WriteLine($"Kategoria: {kategoria}");
}
Przykładowy przebieg
Punkty w turnieju (0-100): 90
Kategoria: Finalista

Dlaczego warunki są w tej kolejności?

Najpierw sprawdzamy poprawność zapisu, potem sens liczby. Wewnątrz poprawnego przypadku progi z >= ustawiamy malejąco. Gdyby pierwszym warunkiem było punkty >= 50, uczestnik z 95 punktami trafiłby do niewłaściwej kategorii. Ostatni else przypisuje kategorię na każdej pozostałej ścieżce.

Użyte metody

Console.Write zwraca void i wypisuje zachętę bez nowej linii. Console.ReadLine zwraca string? — tekst lub null. int.TryParse zwraca bool oraz zapisuje przez out liczbę albo 0 przy niepowodzeniu. Console.WriteLine zwraca void i wypisuje kategorię w nowym wierszu. Interpolacja $”…{kategoria}…” jest składnią języka do wstawiania wartości w tekst.

Testy granic: 49 → trening, 50 → dobry wynik, 89 → dobry wynik, 90 → finał, 100 → finał. Dodatkowo -1, 101 oraz kot muszą być odrzucone. Te testy sprawdzają przejścia między kategoriami.

CZĘSTE BŁĘDY

Na czym się najczęściej potykamy

BłądCo się dzieje i jak poprawić
if (x = 5) CS0029 — jeden znak = to przypisanie. Porównanie to ==. Na szczęście C# nie pozwoli tego uruchomić.
if (warunek); Średnik kończy instrukcję if. Blok w klamrach wykona się zawsze, niezależnie od warunku. Kompilator ostrzega (CS0642), ale program się zbuduje.
if (5 < x < 10) Zapis matematyczny, którego C# nie rozumie. Najpierw policzyłby 5 < x (wynik bool), a potem próbował porównać prawdę z liczbą — CS0019. Poprawnie: if (x > 5 && x < 10).
zła kolejność progów Kompiluje się i uruchamia, ale zawsze wpada w pierwszy, najłagodniejszy warunek. Przy >= zaczynaj od największej wartości.
if (0.1 + 0.2 == 0.3) Fałsz — liczby double są przybliżeniami. Porównuj różnicę: if (Math.Abs(a - b) < 0.0001). Do pieniędzy używaj decimal.
brak klamer przy dwóch instrukcjach Do if bez klamer należy tylko pierwsza instrukcja. Druga wykona się zawsze, choć wcięcie sugeruje co innego.
zmienna zadeklarowana w bloku CS0103 po klamrze zamykającej. Deklaruj przed if, w blokach tylko przypisuj.
brak else przy przypisaniu CS0165: Use of unassigned local variable. Dopisz else albo nadaj wartość początkową przy deklaracji.
if (odpowiedz == "tak") na odpowiedzi „TAK” Porównanie napisów rozróżnia wielkość liter, więc „TAK” nie przejdzie. Użyj string.Equals(odpowiedz, "tak", StringComparison.OrdinalIgnoreCase).
else if zamiast osobnych if Odwrotna pułapka: gdy warunki nie wykluczają się nawzajem i każdy ma zadziałać niezależnie, drabinka jest błędem — po pierwszym trafieniu reszta zostanie pominięta. Wtedy pisz osobne, niepowiązane instrukcje if.
ZADANIA

Zadania

ZAD 1Parzysta, nieparzysta czy zero★☆☆

Wczytaj liczbę całkowitą i wypisz, czy jest parzysta, nieparzysta, czy równa zeru. Zero potraktuj jako osobny przypadek.

ZAD 2Największa z trzech★☆☆

Wczytaj trzy liczby i wypisz największą. Rozwiąż zadanie na dwa sposoby: instrukcjami if oraz metodą Math.Max.

ZAD 3Ocena z procentów★★☆

Wczytaj wynik procentowy (0–100) i wypisz ocenę według progów: 98 i więcej → 6, od 90 → 5, od 75 → 4, od 50 → 3, od 30 → 2, poniżej → 1. Odrzuć wartości spoza zakresu.

ZAD 4Rok przestępny★★☆

Wczytaj rok i sprawdź, czy jest przestępny. Reguła: rok podzielny przez 4 jest przestępny, chyba że jest podzielny przez 100 — wtedy musi być jeszcze podzielny przez 400.

ZAD 5Bilet do kina★★☆

Bilet normalny kosztuje 25 zł. Osobom do 18. roku życia oraz od 65. przysługuje bilet ulgowy za 18 zł. We wtorki obowiązuje dodatkowo 20% rabatu od wyliczonej ceny. Wczytaj wiek i odpowiedź na pytanie o wtorek, po czym wypisz cenę.

ZAD 6Równanie kwadratowe★★★

Wczytaj współczynniki a, b, c i rozwiąż równanie ax² + bx + c = 0. Uwzględnij trzy przypadki delty oraz sytuację, gdy a wynosi zero (równanie nie jest wtedy kwadratowe).

PODSUMOWANIE

Co trzeba zapamiętać

  • if wykonuje blok, gdy warunek bool jest prawdziwy.
  • W drabince else if wykonuje się pierwsza pasująca gałąź.
  • Dla progów >= sprawdzaj zwykle większe wartości najpierw; dla < mniejsze. Zawsze sprawdź granice.
  • Osobne if służą niezależnym sprawdzeniom.
  • Średnik bezpośrednio po if kończy pustą instrukcję i zwykle oznacza pomyłkę.
  • Zmienna lokalna wewnątrz bloku nie jest dostępna z zewnątrz tego bloku.
  • ?: wybiera jedną z dwóch wartości; nie trzeba nim zastępować każdej instrukcji if.
  • Przed użyciem wyniku parsowania sprawdź bool i reguły zakresu.
Postęp zapisuje się w tej przeglądarce.