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 8:

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 10. 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

Kalkulator BMI z kategorią

W lekcji 10 program liczył BMI i wypisywał zakresy jako zwykły tekst — uczeń musiał sam sprawdzić, w którym przedziale się znalazł. Teraz program zrobi to za niego.

Program.cs
Console.WriteLine("=== KALKULATOR BMI ===");
Console.WriteLine();

Console.Write("Wzrost w metrach (np. 1,75): ");
bool okWzrost = double.TryParse(Console.ReadLine(), out double wzrost);

Console.Write("Waga w kilogramach (np. 68,5): ");
bool okWaga = double.TryParse(Console.ReadLine(), out double waga);

Console.WriteLine();

// 1. najpierw odrzucamy dane, ktorych nie da sie uzyc
if (!okWzrost || !okWaga)
{
    Console.WriteLine("Wzrost i waga musza byc liczbami.");
}
else if (wzrost <= 0.5 || wzrost >= 2.5 || waga <= 20 || waga >= 400)
{
    Console.WriteLine("Podane wartosci sa poza realnym zakresem.");
}
else
{
    // 2. dopiero tutaj wiemy, ze liczenie ma sens
    double bmi = waga / (wzrost * wzrost);

    string kategoria;
    if (bmi < 18.5)
    {
        kategoria = "niedowaga";
    }
    else if (bmi < 25.0)
    {
        kategoria = "waga prawidlowa";
    }
    else if (bmi < 30.0)
    {
        kategoria = "nadwaga";
    }
    else
    {
        kategoria = "otylosc";
    }

    Console.WriteLine($"BMI:       {bmi:F1}");
    Console.WriteLine($"Kategoria: {kategoria}");

    // 3. krotki komentarz - operator warunkowy w interpolacji
    Console.WriteLine($"To wynik {(kategoria == "waga prawidlowa" ? "w normie" : "poza norma")}.");
}
wynik w konsoli
=== KALKULATOR BMI ===

Wzrost w metrach (np. 1,75): 1,68
Waga w kilogramach (np. 68,5): 59

BMI:       20,9
Kategoria: waga prawidlowa
To wynik w normie.

Omówienie

FragmentCo robi i dlaczego tak
if (!okWzrost || !okWaga) Najpierw odsiewamy dane, których w ogóle nie da się użyć. Ta kolejność jest ważna: gdyby sprawdzanie zakresu było pierwsze, porównywalibyśmy zero z nieudanej konwersji i program wypisałby mylący komunikat o „nierealnym zakresie”.
else if (wzrost <= 0.5 || ...) Druga bariera: liczba jest, ale bez sensu. Warunek łapie także zero i wartości ujemne, więc nie trzeba ich sprawdzać osobno.
string kategoria; Deklaracja przed drabinką, bo wartość jest potrzebna po jej zakończeniu. Ostatni else gwarantuje, że każda ścieżka coś przypisze — bez niego byłby błąd CS0165.
bmi < 18.5, potem < 25.0, potem < 30.0 Progi z < układamy rosnąco. Każdy kolejny warunek działa już w cieniu poprzedniego: gdy sprawdzamy bmi < 25.0, wiadomo, że BMI wynosi co najmniej 18,5.
{(kategoria == "waga prawidlowa" ? ... )} Operator warunkowy wstawiony w interpolację. Nawias jest konieczny, żeby dwukropek nie został wzięty za specyfikator formatu.

Dlaczego progi to 18.5, a nie 18,5

W kodzie źródłowym liczby ułamkowe zawsze zapisujemy z kropką — to część składni języka, niezależna od kraju. Przecinek pojawia się dopiero przy wyświetlaniu i przy wczytywaniu z klawiatury na polskim systemie.

Czego temu programowi wciąż brakuje

Przy błędnych danych program kończy się bez drugiej szansy. Żeby pytać do skutku, potrzebna jest pętla — dokładnie o tym jest lekcja 14.

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.

pokaż rozwiązanie
Console.Write("Podaj liczbe calkowita: ");
bool ok = int.TryParse(Console.ReadLine(), out int liczba);

if (!ok)
{
    Console.WriteLine("To nie jest liczba calkowita.");
}
else if (liczba == 0)
{
    Console.WriteLine("Zero.");
}
else if (liczba % 2 == 0)
{
    Console.WriteLine("Liczba parzysta.");
}
else
{
    Console.WriteLine("Liczba nieparzysta.");
}

Sprawdzenie liczba == 0 musi być przed testem parzystości — zero jest parzyste, więc odwrotna kolejność nigdy nie pokazałaby komunikatu o zerze.

Uwaga na liczby ujemne: -7 % 2 daje w C# -1, a nie 1. Dlatego porównujemy z zerem (% 2 == 0), a nie z jedynką — zapis liczba % 2 == 1 zawiódłby dla liczb ujemnych.

ZAD 2Największa z trzech★☆☆

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

pokaż rozwiązanie
Console.Write("a = ");
double.TryParse(Console.ReadLine(), out double a);
Console.Write("b = ");
double.TryParse(Console.ReadLine(), out double b);
Console.Write("c = ");
double.TryParse(Console.ReadLine(), out double c);

// sposob 1: instrukcje warunkowe
double max = a;

if (b > max)
{
    max = b;
}
if (c > max)
{
    max = c;
}

Console.WriteLine($"Najwieksza (if):      {max}");

// sposob 2: gotowa metoda
Console.WriteLine($"Najwieksza (Math.Max): {Math.Max(a, Math.Max(b, c))}");

W pierwszym sposobie nie używamy else if — oba warunki mają zostać sprawdzone niezależnie. To dokładnie ten przypadek z tabeli błędów: drabinka po pierwszym trafieniu pominęłaby drugie porównanie i dla danych 1, 5, 9 zwróciłaby 5.

Math.Max przyjmuje tylko dwa argumenty, więc dla trzech liczb wywołujemy ją zagnieżdżoną. Zaczynamy od max = a, a nie od zera — dla samych liczb ujemnych wynik z zerem byłby błędny.

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.

pokaż rozwiązanie
Console.Write("Wynik w procentach (0-100): ");
bool ok = int.TryParse(Console.ReadLine(), out int procent);

if (!ok)
{
    Console.WriteLine("Podaj liczbe calkowita.");
}
else if (procent < 0 || procent > 100)
{
    Console.WriteLine("Wynik musi miescic sie w zakresie 0-100.");
}
else
{
    int ocena;

    if (procent >= 98)      { ocena = 6; }
    else if (procent >= 90) { ocena = 5; }
    else if (procent >= 75) { ocena = 4; }
    else if (procent >= 50) { ocena = 3; }
    else if (procent >= 30) { ocena = 2; }
    else                    { ocena = 1; }

    Console.WriteLine($"Wynik {procent}% to ocena {ocena}.");
    Console.WriteLine(procent >= 50 ? "Egzamin zdany." : "Egzamin niezdany.");
}
wynik w konsoli
Wynik w procentach (0-100): 82
Wynik 82% to ocena 4.
Egzamin zdany.

Przy krótkich blokach klamry można zmieścić w jednej linii i wyrównać w kolumnie — drabinka staje się wtedy czytelna jak tabela. To jedyny przypadek, w którym warto odejść od standardowego układu klamer.

Progi z >= ustawiamy malejąco. Ostatni else bez warunku obsługuje resztę, więc zmienna ocena na pewno dostanie wartość.

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.

pokaż rozwiązanie
Console.Write("Podaj rok: ");
bool ok = int.TryParse(Console.ReadLine(), out int rok);

if (!ok || rok < 1)
{
    Console.WriteLine("Podaj poprawny rok (liczba dodatnia).");
}
else
{
    bool przestepny;

    if (rok % 400 == 0)
    {
        przestepny = true;
    }
    else if (rok % 100 == 0)
    {
        przestepny = false;
    }
    else if (rok % 4 == 0)
    {
        przestepny = true;
    }
    else
    {
        przestepny = false;
    }

    Console.WriteLine($"Rok {rok} {(przestepny ? "JEST" : "NIE JEST")} przestepny.");

    // ten sam wynik jednym wyrazeniem logicznym
    bool krocej = rok % 4 == 0 && (rok % 100 != 0 || rok % 400 == 0);
    Console.WriteLine($"Sprawdzenie: {krocej}");

    // i gotowa metoda z biblioteki
    Console.WriteLine($"DateTime.IsLeapYear: {DateTime.IsLeapYear(rok)}");
}
wynik dla roku 2024
Podaj rok: 2024
Rok 2024 JEST przestepny.
Sprawdzenie: True
DateTime.IsLeapYear: True
pierwsza linia wyniku dla trzech lat kontrolnych
Rok 2024 JEST przestepny.
Rok 1900 NIE JEST przestepny.
Rok 2000 JEST przestepny.

Drabinka zaczyna się od najbardziej szczegółowego warunku (podzielność przez 400), bo to on jest wyjątkiem od wyjątku. Odwrotna kolejność dałaby dla roku 2000 błędną odpowiedź.

Wersja jednowierszowa robi to samo, ale wymaga nawiasu wokół || — bez niego && ma wyższy priorytet i warunek znaczyłby coś zupełnie innego. DateTime.IsLeapYear to gotowa metoda z biblioteki, której w prawdziwym programie należałoby po prostu użyć.

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ę.

pokaż rozwiązanie
const decimal CenaNormalna = 25.00m;
const decimal CenaUlgowa = 18.00m;
const decimal RabatWtorkowy = 0.20m;

Console.Write("Wiek: ");
bool ok = int.TryParse(Console.ReadLine(), out int wiek);

Console.Write("Czy dzis wtorek? (tak/nie): ");
string odp = Console.ReadLine() ?? "";
bool wtorek = string.Equals(odp, "tak", StringComparison.OrdinalIgnoreCase);

if (!ok || wiek < 0 || wiek > 120)
{
    Console.WriteLine("Podaj poprawny wiek.");
}
else
{
    bool ulga = wiek < 18 || wiek >= 65;
    decimal cena = ulga ? CenaUlgowa : CenaNormalna;

    if (wtorek)
    {
        cena = cena - cena * RabatWtorkowy;
    }

    Console.WriteLine();
    Console.WriteLine($"Bilet:  {(ulga ? "ulgowy" : "normalny")}");
    Console.WriteLine($"Wtorek: {(wtorek ? "tak" : "nie")}");
    Console.WriteLine($"Cena:   {cena:F2} zl");
}
wynik dla wieku 16 i odpowiedzi „tak”
Wiek: 16
Czy dzis wtorek? (tak/nie): tak

Bilet:  ulgowy
Wtorek: tak
Cena:   14,40 zl

Zadanie ma dwie niezależne decyzje, więc nie łączymy ich w jedną drabinkę: najpierw ustalamy cenę podstawową, potem — osobno — ewentualny rabat. Gdybyśmy wypisali wszystkie kombinacje (ulgowy we wtorek, ulgowy nie we wtorek, normalny we wtorek, normalny nie we wtorek), przy trzeciej zmiennej byłoby ich już osiem.

Warunek wiek < 18, a nie <= 18: „do 18. roku życia” znaczy, że osiemnastolatek płaci już pełną cenę. Kwoty trzymamy w decimal z przyrostkiem m, bo to pieniądze.

Sprawdź sam: dla wieku 30 i odpowiedzi „tak” wyjdzie 20,00 zł, a dla wieku 70 poza wtorkiem — 18,00 zł.

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

pokaż rozwiązanie
Console.Write("a = ");
double.TryParse(Console.ReadLine(), out double a);
Console.Write("b = ");
double.TryParse(Console.ReadLine(), out double b);
Console.Write("c = ");
double.TryParse(Console.ReadLine(), out double c);

Console.WriteLine();

if (a == 0)
{
    // rownanie liniowe: bx + c = 0
    if (b == 0)
    {
        Console.WriteLine(c == 0
            ? "Kazda liczba jest rozwiazaniem."
            : "Rownanie sprzeczne - brak rozwiazan.");
    }
    else
    {
        Console.WriteLine($"Rownanie liniowe, x = {-c / b:F2}");
    }
}
else
{
    double delta = b * b - 4 * a * c;

    Console.WriteLine($"delta = {delta:F2}");

    if (delta < 0)
    {
        Console.WriteLine("Delta ujemna - brak rozwiazan rzeczywistych.");
    }
    else if (delta == 0)
    {
        double x0 = -b / (2 * a);
        Console.WriteLine($"Jedno rozwiazanie: x = {x0:F2}");
    }
    else
    {
        double pierwiastek = Math.Sqrt(delta);
        double x1 = (-b - pierwiastek) / (2 * a);
        double x2 = (-b + pierwiastek) / (2 * a);

        Console.WriteLine($"Dwa rozwiazania: x1 = {x1:F2}, x2 = {x2:F2}");
    }
}
wynik dla a = 1, b = -5, c = 6
a = 1
b = -5
c = 6

delta = 1,00
Dwa rozwiazania: x1 = 2,00, x2 = 3,00

Sprawdzenie a == 0 jest pierwsze i jest konieczne — bez niego wzór -b / (2 * a) dzieliłby przez zero. Dla typu double nie skończyłoby się to błędem, tylko wynikiem Infinity albo NaN, co jest gorsze: program nie zgłosiłby awarii, tylko wypisał bezsens.

Porównanie delta == 0 to świadome uproszczenie szkolne. Przy współczynnikach z ułamkami delta rzadko wychodzi dokładnie zero, więc w poważnym programie napisalibyśmy Math.Abs(delta) < 0.000001.

Math.Sqrt liczymy raz i zapisujemy do zmiennej. Dwukrotne wywołanie tej samej metody z tym samym argumentem to nie błąd, ale niepotrzebna praca i dłuższy zapis.

Zwróć uwagę na kolejność wypisywania: x1 liczone z minusem wychodzi mniejsze, więc rozwiązania pojawiają się rosnąco.

PODSUMOWANIE

Co trzeba zapamiętać

  • Warunek w if musi być typu bool — liczba się nie nada, a if (x = 5) nie skompiluje się w ogóle.
  • Po nawiasie warunku nie ma średnika.
  • Klamry stawiaj zawsze, nawet przy jednej instrukcji.
  • Drabinka else if zatrzymuje się na pierwszym prawdziwym warunku — kolejność decyduje o wyniku. Przy >= idź od największego progu, przy < od najmniejszego.
  • Gdy warunki mają być sprawdzone niezależnie, używaj osobnych instrukcji if, nie drabinki.
  • Ostatni else gwarantuje, że zmienna dostanie wartość — bez niego grozi CS0165.
  • Zmienna zadeklarowana w klamrach znika po ich zamknięciu.
  • Operator ?: zastępuje if/else tam, gdzie chodzi o wybór wartości — ale nie zagnieżdżaj go.
  • Liczb double nie porównuj przez ==; użyj Math.Abs(a - b) < tolerancja.