Instrukcje warunkowe w C#

Wprowadzenie – Po co nam instrukcje warunkowe?

Instrukcje warunkowe pozwalają programowi podejmować decyzje w zależności od tego, czy pewne warunki są spełnione (prawdziwe), czy nie. Dzięki nim program może wykonywać różne działania w zależności od danych wejściowych.

Analogia z życiem codziennym:

W życiu codziennym również podejmujemy decyzje na podstawie warunków:

  • Jeśli pada deszcz, to biorę parasol
  • Jeśli mam lekcje, to uczę się
  • Jeśli jest zimno, to zakładam kurtkę, w przeciwnym razie wychodzę w koszulce

W programowaniu robimy to samo za pomocą instrukcji warunkowych – pozwalają one programowi „myśleć” i reagować na różne sytuacje.

Przepływ sterowania

Bez instrukcji warunkowych program wykonuje kod sekwencyjnie – linia za linią. Instrukcje warunkowe wprowadzają rozgałęzienia, pozwalając programowi wybrać różne ścieżki wykonania.

// Kod bez warunków - zawsze wykonuje wszystkie instrukcje
Console.WriteLine("Sprawdzam pogodę...");
Console.WriteLine("Wychodzę z domu");

// Kod z warunkami - może wykonać różne instrukcje
Console.WriteLine("Sprawdzam pogodę...");
if (padaDeszcz)
{
    Console.WriteLine("Biore parasol");
}
Console.WriteLine("Wychodzę z domu");

Instrukcja if – podstawa decyzji

Najprostsza instrukcja warunkowa to if, co w języku angielskim oznacza „jeśli”.

Składnia:

if (warunek)
{
    // kod do wykonania, gdy warunek jest prawdziwy
}

Podstawowy przykład:

int temperatura = 25;

if (temperatura > 20)
{
    Console.WriteLine("Jest ciepło.");
}

Console.WriteLine("Program zakończony."); // Ta linia wykona się zawsze

Wyjaśnienie:

  1. if (temperatura > 20) – program sprawdza, czy wartość zmiennej temperatura jest większa niż 20
  2. Jeśli warunek jest prawdziwy (25 > 20), wykonuje kod w nawiasach klamrowych
  3. Program kontynuuje wykonywanie pozostałych instrukcji

Warunki jako wyrażenia logiczne

Warunek w if musi być wyrażeniem logicznym – czyli zwracać true lub false:

int wiek = 17;
bool maLicencje = false;
string imie = "Anna";

// Różne typy warunków
if (wiek >= 18)                    // porównanie liczbowe
if (maLicencje == true)            // porównanie logiczne (można skrócić do: if (maLicencje))
if (imie == "Anna")                // porównanie tekstowe
if (imie.Length > 3)               // wywołanie metody zwracającej bool

Gdy warunek jest fałszywy:

int temperatura = 15;

if (temperatura > 20)  // 15 > 20 to false
{
    Console.WriteLine("Jest ciepło.");  // Ta linia się NIE wykona
}

Console.WriteLine("Sprawdzanie zakończone."); // Ta linia się wykona

Instrukcja else – alternatywa

Czasami chcemy, aby program zrobił coś innego, gdy warunek nie jest spełniony. Używamy wtedy instrukcji else.

Składnia:

if (warunek)
{
    // kod gdy warunek prawdziwy
}
else
{
    // kod gdy warunek fałszywy
}

Przykład z else:

int temperatura = 15;

if (temperatura > 20)
{
    Console.WriteLine("Jest ciepło.");
}
else
{
    Console.WriteLine("Jest zimno.");
}

// Wynik: "Jest zimno." (bo 15 <= 20)

Praktyczny przykład – system logowania:

string haslo = "abc123";
string wprowadzoneHaslo = "abc123";

if (wprowadzoneHaslo == haslo)
{
    Console.WriteLine("✓ Logowanie udane! Witamy w systemie.");
}
else
{
    Console.WriteLine("✗ Błędne hasło. Spróbuj ponownie.");
}

Instrukcja else if – wiele warunków

Gdy chcemy sprawdzić więcej niż jeden warunek, używamy else if.

Składnia:

if (pierwszy_warunek)
{
    // kod dla pierwszego warunku
}
else if (drugi_warunek)
{
    // kod dla drugiego warunku
}
else if (trzeci_warunek)
{
    // kod dla trzeciego warunku
}
else
{
    // kod gdy żaden warunek nie jest spełniony
}

Przykład z klasyfikacją temperatury:

int temperatura = 10;

if (temperatura > 30)
{
    Console.WriteLine("🔥 Jest bardzo gorąco! Zostań w cieniu.");
}
else if (temperatura > 20)
{
    Console.WriteLine("☀️ Jest ciepło. Idealny dzień na spacer.");
}
else if (temperatura > 10)
{
    Console.WriteLine("🧥 Jest chłodno. Weź kurtkę.");
}
else if (temperatura > 0)
{
    Console.WriteLine("🥶 Jest zimno. Ubierz się ciepło!");
}
else
{
    Console.WriteLine("❄️ Mróz! Uważaj na oblodzone drogi.");
}

Ważne: Warunki sprawdzane są po kolei. Gdy pierwszy prawdziwy warunek zostanie znaleziony, pozostałe nie są już sprawdzane.

Przykład – system ocen:

int punkty = 85;
char ocena;

if (punkty >= 90)
{
    ocena = 'A';
    Console.WriteLine($"Brawo! Otrzymujesz ocenę {ocena}");
}
else if (punkty >= 80)
{
    ocena = 'B'; 
    Console.WriteLine($"Dobrze! Otrzymujesz ocenę {ocena}");
}
else if (punkty >= 70)
{
    ocena = 'C';
    Console.WriteLine($"Przeciętnie. Otrzymujesz ocenę {ocena}");
}
else if (punkty >= 60)
{
    ocena = 'D';
    Console.WriteLine($"Słabo. Otrzymujesz ocenę {ocena}");
}
else
{
    ocena = 'F';
    Console.WriteLine($"Niestety, nie zdałeś. Ocena: {ocena}");
}

Operatory logiczne – złożone warunki

Czasami potrzebujemy bardziej skomplikowanych warunków. Operatory logiczne pozwalają łączyć wiele warunków:

Operator && (AND – „i”)

Oba warunki muszą być prawdziwe:

int temperatura = 18;
bool deszcz = true;

if (temperatura > 15 && deszcz == true)
{
    Console.WriteLine("🌧️ Jest ciepło, ale pada deszcz. Weź parasol!");
}

// Inne przykłady z &&
int wiek = 20;
bool maDownod = true;

if (wiek >= 18 && maDownod)
{
    Console.WriteLine("✓ Możesz kupić alkohol");
}

// System dostępu
string uzytkownik = "admin";
string haslo = "pass123";

if (uzytkownik == "admin" && haslo == "pass123")
{
    Console.WriteLine("🔓 Dostęp przyznany do panelu administratora");
}

Operator || (OR – „lub”)

Przynajmniej jeden warunek musi być prawdziwy:

string dzienTygodnia = "Sobota";

if (dzienTygodnia == "Sobota" || dzienTygodnia == "Niedziela")
{
    Console.WriteLine("🎉 Dzisiaj jest weekend! Masz wolne od szkoły.");
}
else
{
    Console.WriteLine("📚 Dzisiaj jest dzień nauki. Musisz iść do szkoły.");
}

// Inne przykłady z ||
int wiek = 15;
bool maZezwolenie = true;

if (wiek >= 18 || maZezwolenie)
{
    Console.WriteLine("✓ Możesz prowadzić pojazd");
}

// Pogoda
bool slonecznie = false;
bool cieplo = true;

if (slonecznie || cieplo)
{
    Console.WriteLine("🏖️ Dobry dzień na wyjście na zewnątrz!");
}

Operator ! (NOT – „nie”)

Zaprzecza warunkom:

bool padaDeszcz = false;
bool maMask = false;

if (!padaDeszcz)  // jeśli NIE pada deszcz
{
    Console.WriteLine("☀️ Możesz wyjść bez parasola");
}

if (!maMask)  // jeśli NIE ma maski
{
    Console.WriteLine("⚠️ Pamiętaj o masce!");
}

// Kombinowanie z innymi operatorami
bool jestWeekend = true;
bool maPrace = false;

if (jestWeekend && !maPrace)  // jest weekend I nie ma pracy
{
    Console.WriteLine("🎮 Czas na odpoczynek!");
}

Złożone przykłady operatorów logicznych:

int wiek = 25;
bool maLicencje = true;
bool maSamochod = true;
bool jestTrzezwy = true;

// Wszystkie warunki muszą być spełnione
if (wiek >= 18 && maLicencje && maSamochod && jestTrzezwy)
{
    Console.WriteLine("🚗 Możesz prowadzić!");
}
else
{
    Console.WriteLine("🚫 Nie możesz prowadzić");
    
    // Sprawdźmy dlaczego
    if (wiek < 18)
        Console.WriteLine("- Jesteś za młody");
    if (!maLicencje)
        Console.WriteLine("- Nie masz prawa jazdy");
    if (!maSamochod)
        Console.WriteLine("- Nie masz samochodu");
    if (!jestTrzezwy)
        Console.WriteLine("- Nie jesteś trzeźwy");
}

Kolejność wykonywania operatorów

Ważne jest zrozumienie kolejności wykonywania operatorów:

  1. ! (NOT)
  2. && (AND)
  3. || (OR)
bool a = true;
bool b = false;
bool c = true;

// Bez nawiasów
bool wynik1 = !a && b || c;    // (!a && b) || c = (false && false) || true = true

// Z nawiasami dla czytelności
bool wynik2 = (!a && b) || c;  // to samo co wyżej, ale jaśniejsze
bool wynik3 = !(a && b) || c;  // !(true && false) || true = true || true = true

Najlepsza praktyka: Używaj nawiasów dla czytelności!

// Trudne do zrozumienia
if (wiek >= 18 && maLicencje || jestInstruktor && !jestPijany)

// Czytelne dzięki nawiasiem
if ((wiek >= 18 && maLicencje) || (jestInstruktor && !jestPijany))

Porównywanie różnych typów danych

Porównywanie liczb:

int a = 10;
double b = 10.5;
decimal c = 15.25m;

if (a < b)        Console.WriteLine("a jest mniejsze od b");
if (b < c)        Console.WriteLine("b jest mniejsze od c");
if (a != b)       Console.WriteLine("a nie równa się b");
if (a == 10)      Console.WriteLine("a równa się 10");

Porównywanie stringów:

string imie1 = "Anna";
string imie2 = "ANNA";
string imie3 = "anna";

// Porównanie z uwzględnieniem wielkości liter
if (imie1 == imie2)  // false - "Anna" != "ANNA"
{
    Console.WriteLine("Imiona są identyczne");
}

// Porównanie bez uwzględnienia wielkości liter
if (imie1.ToLower() == imie2.ToLower())  // true
{
    Console.WriteLine("Imiona są takie same (ignorując wielkość liter)");
}

// Lub użycie Equals z parametrem
if (string.Equals(imie1, imie2, StringComparison.OrdinalIgnoreCase))
{
    Console.WriteLine("Imiona są takie same");
}

Porównywanie znaków:

char litera = 'A';
char cyfra = '5';

if (char.IsLetter(litera))
    Console.WriteLine($"{litera} to litera");
    
if (char.IsDigit(cyfra))
    Console.WriteLine($"{cyfra} to cyfra");
    
if (litera >= 'A' && litera <= 'Z')
    Console.WriteLine($"{litera} to wielka litera");

Skrócona składnia dla instrukcji if

Instrukcje jednoliniowe (bez nawiasów klamrowych):

int temperatura = 25;

// Pełna składnia
if (temperatura > 20)
{
    Console.WriteLine("Jest ciepło");
}

// Skrócona składnia (tylko dla jednej instrukcji!)
if (temperatura > 20)
    Console.WriteLine("Jest ciepło");

// UWAGA: To może być mylące, lepiej zawsze używać nawiasów:
if (temperatura > 20)
{
    Console.WriteLine("Jest ciepło");  // ✅ Czytelne i bezpieczne
}

Operator warunkowy (ternary operator):

Gdy mamy prostą decyzję if-else, możemy użyć skróconej formy:

// Tradycyjne if-else
int wiek = 17;
string status;
if (wiek >= 18)
{
    status = "pełnoletni";
}
else
{
    status = "niepełnoletni";
}

// Operator warunkowy (warunek ? wartość_jeśli_prawda : wartość_jeśli_fałsz)
string status2 = (wiek >= 18) ? "pełnoletni" : "niepełnoletni";
Console.WriteLine($"Status: {status2}");

// Inne przykłady
int a = 10, b = 20;
int wieksza = (a > b) ? a : b;  // zwraca większą liczbę

string pogoda = "słonecznie";
string ubiór = (pogoda == "deszcz") ? "kurtka" : "koszulka";

Zagnieżdżone instrukcje if

Możemy umieszczać instrukcje if wewnątrz innych instrukcji if:

int wiek = 20;
bool maLicencje = true;
int doswiadczenie = 2; // lata

if (wiek >= 18)  // pierwszy poziom
{
    Console.WriteLine("Jest pełnoletni");
    
    if (maLicencje)  // drugi poziom
    {
        Console.WriteLine("Ma prawo jazdy");
        
        if (doswiadczenie >= 2)  // trzeci poziom
        {
            Console.WriteLine("Może prowadzić samotnie");
        }
        else
        {
            Console.WriteLine("Powinien jeździć z doświadczonym kierowcą");
        }
    }
    else
    {
        Console.WriteLine("Musi zdobyć prawo jazdy");
    }
}
else
{
    Console.WriteLine("Jest niepełnoletni - nie może prowadzić");
}

Uwaga: Zbyt głębokie zagnieżdżenie (więcej niż 3-4 poziomy) sprawia, że kod staje się trudny do czytania. Lepiej używać else if lub podzielić na funkcje.

Instrukcja switch – alternatywa dla wielu else if

Gdy mamy wiele warunków sprawdzających tę samą zmienną, switch może być bardziej czytelny:

int dzienTygodnia = 3;
string nazwaDnia;

// Używając if-else if
if (dzienTygodnia == 1)
    nazwaDnia = "Poniedziałek";
else if (dzienTygodnia == 2)
    nazwaDnia = "Wtorek";
else if (dzienTygodnia == 3)
    nazwaDnia = "Środa";
else if (dzienTygodnia == 4)
    nazwaDnia = "Czwartek";
else if (dzienTygodnia == 5)
    nazwaDnia = "Piątek";
else if (dzienTygodnia == 6)
    nazwaDnia = "Sobota";
else if (dzienTygodnia == 7)
    nazwaDnia = "Niedziela";
else
    nazwaDnia = "Nieprawidłowy dzień";

// Używając switch (czytelniej!)
switch (dzienTygodnia)
{
    case 1:
        nazwaDnia = "Poniedziałek";
        break;
    case 2:
        nazwaDnia = "Wtorek";
        break;
    case 3:
        nazwaDnia = "Środa";
        break;
    case 4:
        nazwaDnia = "Czwartek";
        break;
    case 5:
        nazwaDnia = "Piątek";
        break;
    case 6:
        nazwaDnia = "Sobota";
        break;
    case 7:
        nazwaDnia = "Niedziela";
        break;
    default:
        nazwaDnia = "Nieprawidłowy dzień";
        break;
}

Console.WriteLine($"Dzisiaj jest {nazwaDnia}");

Switch z string:

string komenda = "ZAPISZ";

switch (komenda.ToUpper())  // konwersja na wielkie litery
{
    case "ZAPISZ":
        Console.WriteLine("💾 Zapisywanie pliku...");
        break;
    case "OTWORZ":
        Console.WriteLine("📂 Otwieranie pliku...");
        break;
    case "DRUKUJ":
        Console.WriteLine("🖨️ Drukowanie...");
        break;
    case "WYJDZ":
        Console.WriteLine("👋 Do widzenia!");
        break;
    default:
        Console.WriteLine("❌ Nieznana komenda");
        break;
}

Najczęste błędy i pułapki

1. Przypisanie zamiast porównania:

int x = 5;

// ❌ Błąd - przypisanie zamiast porównania
if (x = 10)  // Błąd kompilacji w C#! (na szczęście)
{
    Console.WriteLine("x równa się 10");
}

// ✅ Poprawne porównanie
if (x == 10)
{
    Console.WriteLine("x równa się 10");
}

2.Porównywanie float/double:

double a = 0.1 + 0.2;
double b = 0.3;

// ❌ Może nie działać jak oczekujemy!
if (a == b)  // może być false przez błąd zaokrąglenia
{
    Console.WriteLine("Liczby są równe");
}

// ✅ Bezpieczne porównanie
if (Math.Abs(a - b) < 0.0001)  // tolerancja błędu
{
    Console.WriteLine("Liczby są w przybliżeniu równe");
}

3.Logika De Morgana:

bool a = true;
bool b = false;

// Te warunki są równoważne:
if (!(a && b))          // NOT (a AND b)
if (!a || !b)           // (NOT a) OR (NOT b)

if (!(a || b))          // NOT (a OR b)  
if (!a && !b)           // (NOT a) AND (NOT b)

4.Short-circuit evaluation:

int x = 0;
int y = 10;

// Bezpieczne - jeśli x == 0, drugi warunek nie będzie sprawdzany
if (x != 0 && y / x > 2)  // nie spowoduje dzielenia przez zero
{
    Console.WriteLine("y/x jest większe od 2");
}

// Niebezpieczne!
if (y / x > 2 && x != 0)  // może spowodować dzielenie przez zero!
{
    Console.WriteLine("To się nie wykona");
}

Najlepsze praktyki

1. Używaj nawiasów klamrowych zawsze:

// ❌ Może prowadzić do błędów
if (warunek)
    Console.WriteLine("Tak");
    Console.WriteLine("To nie należy do if!"); // To się zawsze wykona!

// ✅ Bezpieczne i czytelne
if (warunek)
{
    Console.WriteLine("Tak");
    Console.WriteLine("To należy do if");
}

2. Używaj nawiasów dla czytelności:

// ❌ Trudne do zrozumienia
if (wiek >= 18 && maLicencje || jestInstruktor && !jestPijany)

// ✅ Czytelne dzięki nawiasem
if ((wiek >= 18 && maLicencje) || (jestInstruktor && !jestPijany))

3. Pozytywne warunki są czytelniejsze:

// ❌ Negatywny warunek - trudniejszy do zrozumienia
if (!nieAktywny)
{
    Console.WriteLine("Użytkownik jest aktywny");
}

// ✅ Pozytywny warunek - czytelniejszy
if (czyAktywny)
{
    Console.WriteLine("Użytkownik jest aktywny");
}

4. Używaj wczesnych zwrotów (early returns):

// ❌ Głębokie zagnieżdżenie
public string SprawdzDostep(int wiek, bool maLicencje, bool jestTrzeźwy)
{
    if (wiek >= 18)
    {
        if (maLicencje)
        {
            if (jestTrzeźwy)
            {
                return "Dostęp przyznany";
            }
            else
            {
                return "Nie jest trzeźwy";
            }
        }
        else
        {
            return "Brak licencji";
        }
    }
    else
    {
        return "Za młody";
    }
}

// ✅ Wczesne zwroty - płaskie i czytelne
public string SprawdzDostep(int wiek, bool maLicencje, bool jestTrzeźwy)
{
    if (wiek < 18)
        return "Za młody";
        
    if (!maLicencje)
        return "Brak licencji";
        
    if (!jestTrzeźwy)
        return "Nie jest trzeźwy";
        
    return "Dostęp przyznany";
}

5. Grupuj powiązane warunki:

// ❌ Rozproszone warunki
if (jestWeekend)
    pusteUlice = true;
if (jestWeekend)
    sklepyZamkniete = true;
if (jestWeekend)
    ludzieSpia = true;

// ✅ Zgrupowane warunki
if (jestWeekend)
{
    pusteUlice = true;
    sklepyZamkniete = true;
    ludzieSpia = true;
}

6. Null-conditional operators (C# 6.0+):

string tekst = null;

// ❌ Może rzucić NullReferenceException
if (tekst.Length > 5)  // BŁĄD jeśli tekst == null
{
    Console.WriteLine("Długi tekst");
}

// ✅ Bezpieczne sprawdzanie
if (tekst != null && tekst.Length > 5)
{
    Console.WriteLine("Długi tekst");
}

// ✅ Jeszcze krócej z operatorem ?.
if (tekst?.Length > 5)
{
    Console.WriteLine("Długi tekst");
}

// Przykład z obiektami
Person person = null;
if (person?.Address?.City == "Warszawa")
{
    Console.WriteLine("Osoba mieszka w Warszawie");
}