Menu

if else w C#: łańcuchy else if, warunki i typowe błędy

Jak działają if, else if i else w C#: dlaczego warunki muszą być typu bool, łączenie testów przez &&, || i !, kiedy nawiasy klamrowe mają znaczenie, klauzule ochronne zamiast głębokiego zagnieżdżania oraz błędy = zamiast == i zbędnego średnika.

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

Instrukcja if wykonuje blok kodu tylko wtedy, gdy warunek jest prawdziwy. else if dodaje kolejne testy, a else łapie wszystko, czego testy nie złapały.

Wynik:

Order 120: shipping 0
Order 75.50: shipping 4.99
Order 20: shipping 9.99

Warunki są sprawdzane od góry do dołu i wygrywa pierwszy prawdziwy. Zamówienie na 120 pasuje do >= 100 i nigdy nie dociera do testu >= 50, dlatego najbardziej szczegółowy warunek idzie pierwszy. else if nie jest osobnym słowem kluczowym: to else, którego ciałem jest kolejny if, więc w C# nie ma elseif ani elif.

Warunki muszą być typu bool

C# nigdy nie traktuje liczby, stringa ani obiektu jako prawdy lub fałszu. Wyrażenie w nawiasach musi mieć typ bool:

int count = 3;
if (count)          // error CS0029: Cannot implicitly convert type 'int' to 'bool'
if (count > 0)      // correct
if (name)           // error: a string is not a bool either
if (name != null)   // correct

To eliminuje całą rodzinę błędów znanych z C i JavaScriptu, gdzie 0, "" i null po cichu liczą się jako fałsz. Ceną jest to, że za każdym razem wypisujesz porównanie, co przy okazji pokazuje intencję następnej osobie czytającej kod.

Łączenie warunków przez &&, || i !

&& (i), || (lub) oraz ! (nie) łączą testy logiczne. && i || działają na skróty: prawa strona wykonuje się tylko wtedy, gdy lewa jeszcze nie przesądziła o wyniku.

Wynik:

maya: welcome
invalid or banned account
invalid or banned account
omar: age 9 rejected
invalid or banned account

Wywołanie z null jest bezpieczne dzięki skróconemu obliczaniu: gdy username != null jest fałszywe, username.Length nigdy nie jest obliczane, więc nie ma NullReferenceException. Zamień kolejność na username.Length >= 3 && username != null, a to samo wywołanie się wywróci.

&& wiąże silniej niż ||, więc a || b && c oznacza a || (b && c). Dodawaj nawiasy zawsze, gdy mieszasz te dwa operatory; kompilator ich nie potrzebuje, ale czytelnicy tak. Jednoznakowe & i | też działają na bool, ale zawsze obliczają obie strony. Używaj ich tylko wtedy, gdy prawa strona ma efekt uboczny, który musi nastąpić.

Nawiasy klamrowe i zasada jednej instrukcji

Nawiasy klamrowe są opcjonalne, gdy gałąź zawiera jedną instrukcję. Bez nich do if należy tylko następna instrukcja, niezależnie od tego, co sugeruje wcięcie:

Wynik:

Reorder email sent
Stock: 12

"Reorder email sent" wypisuje się, choć stan magazynu jest w porządku: drugie WriteLine jest poza if, mimo wcięcia. Jednolinijkowe zabezpieczenie w stylu if (x == null) return; jest w porządku bez nawiasów; wszystko, co może urosnąć o drugą linię, powinno je dostać.

Pokrewny błąd to średnik tuż po warunku. if (stock < 5); kończy if pustą instrukcją, a następujący po nim blok wykonuje się bezwarunkowo. Kompilator zgłasza to ostrzeżeniem CS0642, Possible mistaken empty statement, które warto traktować jak błąd.

= a == w warunku

= przypisuje, == porównuje. Dla większości typów omyłkowe = się nie kompiluje, bo wynikiem przypisania jest przypisana wartość, a int nie jest bool:

int score = 10;
if (score = 100) { }   // error CS0029: Cannot implicitly convert type 'int' to 'bool'

Przy zmiennej typu bool samo przypisanie jest typu bool, więc literówka się kompiluje:

Wynik:

Access granted
isAdmin is now True

Warunek nadpisał isAdmin, a potem sprawdził nową wartość. Kompilator ostrzega (CS0665, przypisanie w wyrażeniu warunkowym jest zawsze stałe), ale kompilacja i tak się udaje. Dla wartości bool w ogóle pomijaj porównanie: if (isAdmin) i if (!isAdmin) czyta się lepiej i nie da się ich w ten sposób pomylić.

Zagnieżdżone if a klauzule ochronne

Każdy poziom zagnieżdżenia to kolejny warunek, który czytelnik musi trzymać w głowie. Gdy każde sprawdzenie odrzuca złe dane wejściowe, wracaj wcześnie zamiast zagnieżdżać główną ścieżkę wewnątrz nich wszystkich:

Wynik:

ordered 2 x keyboard
error: no product
error: quantity must be positive
error: insufficient balance

Zagnieżdżona wersja tej samej metody miałaby trzy poziomy if, właściwą pracę w najgłębszym bloku i trzy gałęzie else rozwijające się na dole. Z klauzulami ochronnymi każda reguła stoi obok swojego komunikatu o błędzie, a ostatnia linia to normalny przypadek. Ten sam pomysł działa w pętlach z continue.

Deklarowanie zmiennych w warunku

Warunek może zadeklarować zmienną, która jest dostępna wewnątrz if. Dwie typowe formy to out var z metody TryParse i wzorzec typu z is:

Wynik:

42: positive number 42
abc: not a number
-7: number -7, not positive
string of length 5

TryParse przy złych danych zwraca false zamiast rzucać wyjątek, dlatego naturalnie pasuje do if. Metody parsowania szczegółowo omawia strona o konwersji typów.

Kiedy użyć czegoś innego

  • Wybór jednej z dwóch wartości: operator warunkowy condition ? a : b jest krótszy niż if, który przypisuje w obu gałęziach.
  • Wiele gałęzi dla jednej wartości: instrukcja switch porównuje wartość ze stałymi przypadkami i łatwiej ją przejrzeć niż dziesięć linii else if.
  • Mapowanie wartości na wynik: wyszukiwanie w Dictionary zastępuje długi łańcuch, gdy gałęzie różnią się tylko danymi.

Najczęściej zadawane pytania

Jak napisać else if w C#?

Pisz else if jako dwa słowa: if (a) { ... } else if (b) { ... } else { ... }. Warunki są sprawdzane od góry do dołu i wykonuje się tylko pierwsza prawdziwa gałąź, więc najbardziej szczegółowy test umieść na początku. Nie ma słowa kluczowego elif ani elseif.

Dlaczego nie mogę napisać if (count) w C#?

Warunek w C# musi być typu bool. W przeciwieństwie do C i JavaScriptu int, string czy obiekt nigdy nie są konwertowane na true lub false, więc if (count) kończy się błędem CS0029, Cannot implicitly convert type 'int' to 'bool'. Napisz test, o który ci chodzi: if (count > 0) albo if (name != null).

Czym różni się && od & w warunku if?

&& działa na skróty: jeśli lewa strona jest fałszywa, prawa nigdy nie jest obliczana, i dlatego s != null && s.Length > 0 jest bezpieczne. & na dwóch wartościach bool za każdym razem oblicza obie strony. To samo dotyczy || i |. W warunkach używaj && i ||, chyba że potrzebujesz efektu ubocznego prawej strony.

Czy nawiasy klamrowe są wymagane w instrukcji if w C#?

Nie. Bez nawiasów klamrowych if obejmuje dokładnie jedną instrukcję. Wcięcie drugiej linii nie dodaje jej do gałęzi, co jest klasycznym źródłem błędów, dlatego większość przewodników stylu zaleca nawiasy klamrowe w każdej gałęzi.

Czy mogę napisać if else w jednej linii w C#?

Do wyboru wartości użyj operatora warunkowego: string label = score >= 50 ? "pass" : "fail";. Do wykonywania instrukcji zapis if (x) DoA(); else DoB(); w jednej linii jest poprawny, ale trudniej go czytać i rozbudowywać niż blok w nawiasach klamrowych.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ