Ta notatka ma na uwadze zrozumienie pewnych podstawowych struktur programowania które można użyć do formowania logiki w kodzie. Kiedy piszemy program to algorytmy które opisujemy kodem są formowane poprzez logiczny przepływ programu bazujący na decyzjach i powtórzeniach. Dokonujemy tego poprzez użycie iteracji na kolekcjach, wykonywanie pętli jak forwhile i do-while, używanie wyrażenia switch, użycie konstrukcji warunkowej if/then, użycie operatorów i wyliczanie wyrażeń.

Pierwszy program

kod w języku C# jest pisany za pomocą serii wyrażen. Specyfikacja języka dzieli te wyrażenia na dwa podstawowe typy: wyrażenia proste i złożone.

Wyrażenia proste

W języku C# proste wyrażenia to te które kończą się znakiem średnika (;) i są używane do obsługi akcji programu jak niżej wymienione:

  • deklaracja zmiennych (declaration statements).
  • przypisanie wartości do zmiennej (assignment statements).
  • wywołanie metody w kodzie.
  • wyrażenie rozgałęziające które zmienia przepływ programu 
    Przykłady prostych wyrażeń:
// variable declaration 
int counter;
float distance;
string firstName;

// assignment
counter = 0;
distance = 4.5;
firstName = "Bill";

// jump statements
break;
return;

// empty statement
privatevoidSomeFunction()
{
while (DoSomething())
{
;
}
}

Wyrażenia złożone

Wyrażenia złożone to takie które mogą ująć lub ujmują jedno lub wiele wyrażeń prostych tworząc blok kodu otoczony poprzez nawiasy klamrowe: {}. typowymi wyrażeniami złożonymi są pętle i struktury decyzyjne jak foreach()if()switchdo() itp.

Przykładem wyrażenia złożonego może być iteracja tablicy wartości i wykonanie akcji używając różnych wyrażeń np:

// check to see how many values in an array are even numbers
int[] numbers = { 5, 24, 36, 19, 45, 60, 78 };
int
evenNums = 0;
foreach (var num in numbers)
{
Console.Writeline(num);
if (num % 2 == 0)
{
evenNums++;
}
}

Kontrola przepływu programu

Wszystkie aplikacje potrzebują pewnych opcji przepływu programu. W programowaniu strukturalnym przepływ programu odbywał się z góry na dół. niestety ta metoda nie rozwiązuje pewnych problemów ze świata realnego. Czasem w aplikacji potrzebujemy podjąć pewne inne akcje w zależności od od wyniku innych akcji. Nazywa się to rozgałęzianiem kodu. Przepływ programu przechodzi do innej lokacji w kodzie i wraca kiedy wykona wszystkie instrukcje które są w nim zawarte lub powtarza pewne linie kodu aż do zakończenia wszystkich zadań.

Instrukcje warunkowe

Instrukcje warunkowe w C# są to te które wyliczają warunek i w jego wyniku wykonują pewną akcję, nie podejmują żadnej akcji lub wybierają pomiędzy dostępnymi akcjami do wykonania. Do wyliczania warunków C# wykorzystuje:

  • operatory relacji
  • wyrażenia logiczne
  • operatory logiczne
  • operatory warunkowe (operator trójargumentowy) 
    Warunki w C# pozwalają na porównanie wartości przechowywanych w zmiennych, stałych i literałach. Zmienna (variable) jest nazwaną lokacją w pamięci która pozwala na zapis wartości w celu późniejszego wykorzystania. Stała (constant) to również miejsce w pamięci ale nie można zmienić jej wartości po zadeklarowaniu.

Operatory relacyjne:

Operator Znaczenie Przykład
< mniejsze niż expr1 < expr2
> większe niż expr1 > expr2
<= mniejsze niż lub równe expr1 <= expr2
>= większe niż lub równe expr1 >= expr2
== równość expr1 == expr2
!= nie równe expr1 != expr2

Operatory logiczne:

Operator Znaczenie Przykład
& Wariant jednoargumentowy zwraca adres swojego argumentu & expr1
& Wariant dwuargumentowy jest operatorem logicznym AND expr1 & expr2
I Dwuargumentowy operator logiczny OR expr1 I expr2
^ Bitowe XOR. Zwraca true gdy tylko jeden operand jest równy true expr1 ^ expr2
! Jednoargumentowy operator negacji !expr1
~ Bitowy operator uzupełnienia ~expr
&& Warunkowy AND wykonujący operację logiczną AND na argumentach bool. Drugi argument jest wyliczany tylko w razie potrzeby (short-circuit evaluation). expr1 && expr2
II Warunkowy OR wykonujący operację logiczną OR na argumentach bool. Wylicza tylko drugi argument jeżeli wystarcza (short-circuit evaluation). expr1 II expr2
true Wskazuje prawdę w wyrażeniu bool success = true;
false Wskazuje fałsz w wyrażeniu bool success = false;

Najczęściej wykorzystywanymi operatorami są (&&) i (||) oraz wartości logiczne true i false. Operatory są optymalizowane w czasie wyliczania wyrażeń, optymalizacja ta jest nazywana short-circuit evaluation. oznacza to, że dla operacji && nie sprawdza się drugiego warunku jeżeli pierwszy jest fałszywy. to uproszczenie daje pewien zysk w wydajności poprzez wyeliminowanie pewnego nakładu pracy jaki komputer musi wykonać. Aby lepiej to zrozumieć należy znać algorytm porównywania wykonywany przez CPU w czasie porównania:

  1. Pobranie instrukcji i załadowanie do pamięci.
  2. Inkrementacja wskaźnika instrukcji.
  3. Odszukanie w pamięci pierwszej wartości, pobranie JEJ z pamięci i umieszczenie w rejestrze.
  4. Dostęp do pamięci w celu pobrania drugiej wartości i umieszczenie jej w rejestrze CPU.
  5. Wykonanie porównania i umieszczenie wyniku w rejestrze CPU.
  6. Wykonanie pop na stosie dla wskaźnika instrukcji aby powrócić do kodu który był wykonywany przed porównaniem.
  7. Zwrócenie wartości porównania do kodu.
  8. Wykonanie następnej instrukcji kodu.

Operator trójargumentowy:

condition ? valueiftrue : valueiffalse

Podejmowanie decyzji w kodzie

Język C# umożliwia programiście wykorzystanie struktur decyzyjnych służących do podejmowania decyzji.

IF

// single if statement syntax
if(condition)
statement;
remaining code statements;

W nawiasach okrągłych instrukcja IF zawiera warunek logiczny który jest wyliczany i jeżeli jego wartość jest prawdą to zostaje wykonane wyrażenie następujące po nawiasie. Jeżeli wartość warunku jest fałszywa to wyrażenie po nawiasie jest pomijane. Używając tej instrukcji zaleca się aby wyrażenie wykonywane jeżeli wynikiem jest true zamykać w nawiasy klamrowe, nawet jeżeli jest to wyrażenie proste.

// single if statement syntax with a statement block
if(condition)
{
statement;
}
remaining code statements;

instrukcje IF mogą być zagnieżdżone.

// nested if statement
if(condition1)
{
if(condition2)
{
statement;
}
outer statement;
}
remaining code statements;

Przykładowy program testujący działanie instrukcji IF:

using System;
internal class Program
{
privatestaticvoid Main(string[] args)
{
// declare some variables for use in the code and assign initial values
int first = 2;
int second = 0;

// use a single if statement to evaluate a condition and output
// some text
// indicating the results
Console.WriteLine("Single if statement");
if (first == 2)
{
Console.WriteLine("The if statement evaluated to true");
}
Console.WriteLine("This line outputs regardless of the if condition");
Console.WriteLine();

// create an if statement that evaluates two conditions and executes
// statements only if both are true
Console.WriteLine("An if statement using && operator.");
if (first == 2 && second == 0)
{
Console.WriteLine("The if statement evaluated to true");
}
Console.WriteLine("This line outputs regardless of the if condition");
Console.WriteLine();

// create nested if statements
Console.WriteLine("Nested if statements.");
if (first == 2)
{
if (second == 0)
{
Console.WriteLine("Both outer and inner conditions are true.");
}
Console.WriteLine("Outer condition is true, inner may be true.");
}
Console.WriteLine("This line outputs regardless of the if condition");
Console.WriteLine();
}
}

Wynik działania kodu:

Single if statement 
The if statement evaluated to true 
This line outputs regardless of the if condition

An if statement using && operator. 
The if statement evaluated to true 
This line outputs regardless of the if condition

Nested if statements. 
Both outer and inner conditions are true. 
Outer condition is true, inner may be true. 
This line outputs regardless of the if condition

Jeżeli chcemy aby w przypadku kiedy wartość wyrażenia warunkowego jest fałszywa wykonywany był inny blok kodu to używamy instrukcji IF-ELSE:

// if-else statement syntax
if
(condition)
{
statement1;
}
else
{
statement2;
}
remaining code statements;

Dla wielu warunków i rozgałęzień kodu można używać instrukcji IF, ELSE IF:

// if-else if statement syntax
if
(condition1)
{
statement1;
}
else if (condition2)
{
statement2;
}
else if (condition3)
{
statement3;
}
else
{
statement4;
}
remaining code statements;

SWITCH

Jeżeli chcemy ten sam warunek poddać testowaniu z różnymi wartościami to możemy użyć instrukcji SWITCH:

// switch statement syntax
switch (condition)
{
case1:
statement1;
break;
case2:
statement2;
break;
case3:
statement3;
break;
default:
defaultStatement;
break;
}

Można również wykonać tą sama instrukcję dla wielu warunków:

switch (number)
{
case0:
case 1:
case 2:
Console.Writeline ("Contained in the set of whole numbers.");
break;
case -1:
case -10:
Console.WriteLine ("Contained in the set of Integers.");
break;
}

Użycie pętli

Użycie pętli w kodzie pozwala na powtórzenie przez aplikację serii instrukcji. Język C# dostarcza cztery struktury pętli:

  • for
  • foreach
  • while
  • do-while 
    Decyzja której z nich użyć zależy od wymagań ale wszystkie z nich dostarczają funkcjonalność powtarzalnego wykonywania instrukcji.

FOR

Pętla FOR pozwala na powtarzalne wykonywanie serii instrukcji do czasu aż nie zostanie spełniony wyznaczony warunek. Pętla FOR składa się z trzech elementów: inicjatora, warunku i iteratora (ang. initializerconditioniterator) oddzielonych znakiem średnika (;). 
// for statement syntax

for(initializer; condition; iterator)
{
statement(s);
}

Inicjator służy do deklaracji i inicjalizacji zmiennych startowych które będą używane w pętli. Warunek określa kiedy pętla ma zostać zatrzymana, natomiast iterator służy do modyfikacji zmiennych w każdym przebiegu pętli. Np:

// Count up to 10 in increments of 2
for(int counter = 0; counter <= 10; counter += 2)
{
Console.WriteLine(counter);
}

Zmienne zadeklarowane w pętli nie są widoczne poza pętlą (scope). 
Przykład nieskończonej się pętli:

// infinite forloopin C#
for(;;)
{
statement;
}

FOREACH

Pętla FOREACH używana jest do iteracji po wszystkich elementach z kolekcji. Kolekcjami zazwyczaj są tablice ale mogą nimi być także inne obiekty .NET implementujące interfejs IEnumerable.

// foreach syntax
foreach(typein collection)
{
statement;
}

Type” jest typem obiektów kolekcji. Przykład wykorzystania pętli:

// foreach loop to average grades in an array
// set up an integer array and assign some values
int[] arrGrades = new int[] {78, 89, 90, 76, 98, 65};
// create three variables to hold the sum, number of grades, and the average
int
total = 0;
int
gradeCount = 0;
double
average = 0.0;
// loop to iterate over each integer value in the array
// foreach doesn't need to know the size initially as it is determined
// at the time the array is accessed.
foreach(int grade in arrGrades)
{
total = total + grade; // add each grade value to total
gradeCount++; // increment counter for use in average
}
average = total / gradeCount; // calculate average of grades
Console.WriteLine(average);

WHILE

Podobnie do pętli FOR pętla WHILE jest wykonywana aż do czasu kiedy warunek który jest zapisany w nawiasie pętli jest spełniony, kiedy warunek zwróci fałsz pętla jest przerywana i przechodzi do dalszego wykonywania instrukcji.

// while statement syntax
while(condition)
{
statement;
}

Przykład:

// while statement example
int someValue = 0;
while(someValue < 10)
{
Console.WriteLine(someValue);
someValue++;
}

DO-WHILE

Pętla DO-WHILE działa podobnie do pętli WHILE z dwoma wyjątkami. Po pierwsze blok instrukcji w tej pętli jest zawsze wykonywany co najmniej jeden raz niezależnie od warunku. Po drugie warunek jest sprawdzany po wykonaniu bloku instrukcji a nie na początku.

//do-whileloop syntax
do
{
statement;
} while (condition);

Przykład:

// do-while statement example
int someValue = 0;
do
{
Console.WriteLine(someValue);
someValue++;
} while (someValue < 10);

Użyteczny przykład użycia DO-WHILE:

// while statement example
char someValue;
do
{
someValue = (char) Console.Read();
Console.WriteLine(someValue);
} while (someValue != 'q');

Podsumowanie

Wyrażenia proste

  • Są zakończone znakiem średnika (;).
  • Zwykle są zapisane w jednej linii.
  • Zwykle używane dla deklaracji i przypisania wartości zmiennej.

Wyrażenia złożone

  • Zwykle zawierają wyrażenia proste w nawiasach klamrowych {}.
  • Mogą być zakończone znakiem średnika (;).

IF-THEN-ELSE

  • Używane do podejmowania decyzji.
  • Wykonuje ścieżkę kodu która spełnia podany warunek.
  • Nie wymaga nawiasów klamrowych ale ich używanie jest dobrą praktyką.
  • Klauzula ELSE IF służy do wybrania alternatywnej ścieżki wykonania kodu.
  • Klauzula ELSE służy do wybrania alternatywnej ścieżki dla fałszywego wyniku warunku.
  • Może być zagnieżdżona.

SWITCH

  • Może testować różne typy danych w warunku.
  • Jest czystszym kodem niż zagnieżdżone instrukcje IF.
  • Może zawierać klauzulę DEFAULT dla przypadku gdy żadna wartość nie spełnia warunku.
  • Wyrażenie BREAK oznacza koniec bloku instrukcji dla spełnionego warunku.

FOR

  • Prosta pętla.
  • Używa inicjatora, warunku i iteratora oddzielonych średnikami i otoczonych okrągłym nawiasem.
  • Może być zagnieżdżona.
  • Warunek jest sprawdzany na początku każdej iteracji.

FOREACH

  • Służy do iterowania po elementach kolekcji implementującej interfejs IEnumerable.
  • Może być zagnieżdżona.

WHILE

  • Inicjator i iterator nie są częścią pętli jak w przypadku FOR.
  • Warunek jest sprawdzany na początku każdej iteracji.

DO-WHILE

  • Podobna do pętli WHILE.
  • Inicjator i iterator nie są częścią pętli jak w przypadku FOR.
  • Warunek jest sprawdzany na końcu iteracji.
  • Kończy się średnikiem (;).