Dotychczas w prezentowanych przykładach instrukcje były wykonywane w kolejności, w jakiej zostały zapisane. Na szczęście to nie koniec możliwości. C# oferuje instrukcje warunkowe, pętle oraz "przełączniki", które pozwalają sterować przepływem programu. Osoby znające rodzinę języków C odnajdą wiele znajomych koncepcji, jednak omówione zostaną również elementy unikalne dla C#.
Zaczynając od podstaw, instrukcje if
oraz else
umożliwiają wykonanie określonego bloku kodu w zależności od przekazanej wartości logicznej (typ bool
- true
dla prawdy lub false
dla fałszu). Dla warunków prawdziwych wykonany zostanie pierwszy blok kodu, w przeciwnym razie uruchomiony zostanie blok kodu po instrukcji else
. Użycie else
jest opcjonalne.
using System;
namespace HelloWorld
{
public class Program
{
public static void Main(string[] args)
{
bool showHello = true;
if (showHello)
{
// Pierwszy blok kodu
Console.WriteLine("Hello world in C#");
}
else
{
// Drugi blok kodu
Console.Write("So... ");
}
Console.Write("Let's start!");
Console.ReadLine();
}
}
}
Blok kodu po instrukcji else
- tj. {
i }
- jest opcjonalny. Jeżeli warunek dotyczy tylko jednej linii kodu, można go pominąć. Jednakże, dla zachowania czytelności często warto użyć bloków kodu. Jak już wspomniano w poprzednim wpisie Komentarze i dyrektywy, kompilator ignoruje białe znaki, w tym wcięcia. Spójrzmy na poniższy przykład.
using System;
namespace HelloWorld
{
public class Program
{
public static void Main(string[] args)
{
bool showHello = false;
if (showHello)
Console.Write("Hello world in C#");
if (1 == 1)
Console.Write("Let's start!");
else
Console.Write("We know what it is in C#");
}
}
}
Jeśli warunek if(showHello)
będzie prawdziwy, chcemy wyświetlić komunikat "Hello world in C#". Następnie, niezależnie od tego, komunikat "Let's start!" wyświetli się zawsze, ponieważ warunek "zawsze prawdziwy" if (1 == 1)
zawsze będzie spełniony. W przeciwnym razie chcemy wyświetlić "We know what it is in C#". Czy oczekiwana ścieżka przepływu dla tego przykładu to wyświetlenie "We know what it is in C#"?
Niestety oczekiwana ścieżka różni się od wykonanej. Dlaczego? Instrukcja else
nie jest powiązana z pierwszym warunkiem, lecz z drugim. Czytelnicy, którzy spróbują wykonać przykład, zapewne zauważą ostrzeżenie. Warunek if(1 == 1)
powoduje, że komunikat "We know what it is in C#" nigdy nie zostanie osiągnięty.
Wstawienie bloków kodu może wydłużyć kod, jednak zdecydowanie zwiększa jego czytelność. Visual Studio umożliwia konfigurację automatycznego formatowania kodu. Szczegółowy opis znajdziesz tutaj: Text Editor Options Dialog Box.
Przykład sterowania przepływem w zależności od czynników zewnętrznych, na przykład wciśnięcia klawisza.
using System;
namespace HelloWorld
{
public static class Program
{
public static void Main()
{
Console.WriteLine("Do you know what a C#?");
Console.Write("Press [Y] yes or [N] no: ");
ConsoleKeyInfo cKeyInfo = Console.ReadKey();
Console.WriteLine();
bool showHello = cKeyInfo.Key == ConsoleKey.Y ? true : false;
if (showHello)
{
Console.WriteLine("Hello world C#");
showHello = false;
if (!showHello)
{
Console.WriteLine("Let's start!");
}
}
else
{
Console.WriteLine("We know what it is C#");
}
Console.ReadKey();
}
}
}
Zaczynamy od wyświetlenia pytania (linie 9, 10). W linii 12 użyta została metoda ReadKey, a jej wynik (struktura reprezentująca wciśnięty klawisz) został przypisany do zmiennej cKeyInfo.
Kolejna linia to ciekawy przykład operatora trójargumentowego.
bool showHello = cKeyInfo.Key == ConsoleKey.Y;
Ten operator jest skróconą formą instrukcji if
, w której wyrażenie przed znakiem ?
(w tym przypadku cKeyInfo.Key == ConsoleKey.Y
) jest oceniane. Musi zwracać typ bool
. Jeśli jest prawdziwe, zwracana jest wartość po znaku ?
(w tym przypadku true
), a w przeciwnym razie wartość po znaku :
(tutaj false
). Warunek sprawdza, czy wciśnięto klawisz Y - jeśli tak, zwraca "prawda", w przeciwnym razie "fałsz".
Dalej, w bloku kodu sprawdzamy, czy wartość w zmiennej showHello to true
. Jeśli tak, wyświetlany jest komunikat "Hello world in C#", a następnie warunek jest negowany przez znak !
, co pozwala na wyświetlenie komunikatu "Let's start!". Na koniec metody Main w linii 31 ponownie używamy ReadKey, aby zatrzymać aplikację przed zamknięciem.
Do tej pory wyrażenia logiczne w instrukcjach warunkowych opierały się na bezpośrednio przekazanych wartościach typu bool
. W kolejnym przykładzie zobaczymy użycie operatorów relacyjnych.
using System;
namespace HelloWorld
{
public static class Program
{
public static void Main()
{
Console.Write("How many years have you been programming in C#? ");
string inputText = Console.ReadLine();
int progYears = default(int);
if (int.TryParse(inputText, out progYears))
{
if (progYears < 0)
{
Console.Write("You can't have less than 0 years of programming.");
}
else
{
if (progYears <= 1)
{
Console.Write("You are a novice programmer.");
}
else
{
if (progYears <= 5)
{
Console.Write("You're gaining experience.");
}
else
{
Console.Write("You're a seasoned programmer.");
}
}
}
}
else
{
Console.Write("The entered value does not represent a number.");
}
Console.ReadKey();
}
}
}
Powyższy kod pyta, jak długo programujesz w C#, a następnie wyświetla odpowiedni komunikat w zależności od odpowiedzi. Zaczynamy od wyświetlenia pytania, a następnie, w linii 11, używamy metody ReadLine, aby odczytać wprowadzony tekst. W pierwszej instrukcji warunkowej wykorzystujemy metodę TryParse, która zwraca "prawdę", jeśli wartość pierwszego parametru (ciągu znaków) można przekształcić na liczbę całkowitą. Więcej informacji o metodzie TryParse znajdziesz w notatce o obsłudze wyjątków oraz konwersji typów liczbowych. Jeżeli użytkownik wprowadzi znaki niemożliwe do konwersji na liczbę całkowitą, pojawi się komunikat "The entered value does not represent a number". W przeciwnym wypadku wykonywane są kolejne instrukcje warunkowe.
Warunek w linii 16 sprawdza, czy zmienna progYears jest mniejsza od zera, zabezpieczając przed wprowadzeniem nieprawidłowej liczby ujemnej. Następnie, jeśli wartość jest równa lub mniejsza niż jeden, pojawia się komunikat "You are a novice programmer". W przeciwnym wypadku sprawdzamy, czy liczba lat jest mniejsza lub równa pięciu, wyświetlając "You're gaining experience", lub "You're a seasoned programmer", jeśli jest to więcej niż pięć lat.
Operatory relacyjne
Nazwa Instrukcja Przykład
---------------------------------------------------
Mniejszy < a < b
Większy > a > b
Mniejszy lub równy <= a <= b
Większy lub równy >= a >= b
Równy == a == b
Różny != a != b
Kod w powyższym przykładzie jest mocno zagnieżdżony, przez co traci na czytelności. Poprawmy to, łącząc instrukcje else
i if
.
using System;
namespace HelloWorld
{
public static class Program
{
public static void Main()
{
Console.Write("How many years have you been programming in C#? ");
string inputText = Console.ReadLine();
int progYears = default(int);
if (!int.TryParse(inputText, out progYears))
{
Console.Write("The entered value does not represent a number.");
}
else if (progYears < 0)
{
Console.Write("You can't have less than 0 years of programming.");
}
else if (progYears <= 1)
{
Console.Write("You are a novice programmer.");
}
else if (progYears <= 5)
{
Console.Write("You're gaining experience.");
}
else
{
Console.Write("You're a seasoned programmer.");
}
Console.ReadKey();
}
}
}
Po modyfikacji kod jest znacznie czytelniejszy. Zmieniono również pierwszy warunek na:
if (!int.TryParse(inputText, out progYears))
Teraz, jeśli wartość nie zostanie przekształcona na liczbę całkowitą, pojawi się komunikat "The entered value does not represent a number".
Jak zachowa się kod, jeśli odwrócimy kolejność warunków?
Wprowadzenie wartości 1 spełnia warunek else if(progYears <= 5)
. Konstrukcja else if
po spełnieniu pierwszej instrukcji pomija pozostałe, więc zamiana instrukcji na if
spowodowałaby wyświetlenie obu komunikatów dla wartości 1: "You're gaining experience" i "You are a novice programmer".
Operatory logiczne umożliwiają tworzenie złożonych warunków, np. if(progYears > 1 && progYears <= 5)
, gdzie sprawdzamy, czy wartość progYears jest większa od 1 i jednocześnie mniejsza lub równa 5. Warunek jest spełniony, tylko gdy obie części są prawdziwe. Sprawdzenie wykonuje się od lewej do prawej strony. Jeśli używamy operatora koniunkcji &&
i pierwsza część jest fałszywa, kolejne nie są już sprawdzane. W przeciwieństwie do operatora alternatywy ||
, gdzie sprawdzanie kontynuowane jest do pierwszej prawdziwej części.
Operatory logiczne
Nazwa Instrukcja Przykład
------------------------------------------------------------------
Negacja logiczna (NOT) ! !a
Koniunkcja warunkowa (AND) && a && b
Alternatywa warunkowa (OR) || a || b
Na zakończenie spróbujmy zmodyfikować kod tak, aby warunek sprawdzał, czy liczba lat znajduje się w określonym zakresie.
using System;
namespace HelloWorld
{
public static class Program
{
public static void Main()
{
Console.Write("How many years have you been programming in C#? ");
string inputText = Console.ReadLine();
int progYears = default(int);
if (!int.TryParse(inputText, out progYears))
{
Console.Write("The entered value does not represent a number.");
}
else if (progYears > 5)
{
Console.Write("You're a seasoned programmer.");
}
else if (progYears > 1 && progYears <= 5)
{
Console.Write("You're gaining experience.");
}
else if (progYears > 0 && progYears <= 1)
{
Console.Write("You are a novice programmer.");
}
else
{
Console.Write("You can't have less than 0 years of programming.");
}
Console.ReadKey();
}
}
}
Instrukcje if
oraz else
pozwalają na określenie różnych ścieżek działania programu w zależności od wartości zmiennych. W kolejnych notatkach omówione zostaną instrukcja "switch" oraz pętle. Zapraszam do dalszej lektury.