8. Příkazy

Příkaz můžeme chápat jako krok v cestě algoritmu. Většinou se provádějí v pořadí, ve kterém jsou v programu zapsány a některé příkazy musejí být ukončeny středníkem. V C# se ovšem vyskytují příkazy, které se takto chovat nemusí. Říkáme jim skokové příkazy. Podrobněji vše probereme níže.

8.1. Základní příkazy

V této sekci si rozebereme základní příkazy jazyka.

8.1.1. Blok (složený příkaz)

Blok je typ příkazu, který může, ale nemusí, reprezentovat další množinu příkazů. Blok označíme stejně jako například v Javě symboly { a }. Dovnitř bloku můžeme vložit další příkazy, popřípadě další blok. Pokud blok neobsahuje žádné příkazy, nazývá se prázdný příkaz. Příklad:

[ukázka kódu]
// priklad bloku
{ 
   Console.WriteLine("Outer block");

   //toto je vnoreny blok
   {
      string text = "Inner block";
      Console.WriteLine(text);
   }
}

8.1.2. Prázdný příkaz

Mohou nastat situace, kdy na někjakém místě programu jazyk C# vyžaduje přítomnost příkazu, ale my nechceme provádět žádný příkaz. Proto v C# existují i prázdné příkazy.

Jak jsme si uvedli v předchozím odstavci, prázdný příkaz můžeme vyjádřit pomocí dvou složených závorek, které nic neobsahují {}. Další možnost, jak vyjádřit prázdný příkaz, je použitím středníku ;. To nám zaručí, že program na daném místě nebude nic provádět.

Příklad, kdy využijeme prázdných příkazů - chceme se dostat v řetězci na první znak, který není mezera (tedy vypustíme z úvodu řetězce mezery):

[ukázka kódu]
int i=-1; 
while((retezec[++i] == ' ') && (i < retezec.Length)) ; // nebo {}

8.1.3. Výrazový příkaz

Pokud k nějakému výrazu připojíme středník, stane se z něj tzv. výrazový příkaz. Výraz v tomto případě musí nějakým způsobem měnit proměnnou, vlastnost apod. Například pokud za přiřazením čísla 1 do proměnné i použijeme středník, dostaneme výrazový příkaz:

[ukázka kódu]
i = 1;

8.1.4. Deklarace lokální proměnné nebo lokální konstanty

I deklaraci lokální proměnné nebo lokální konstanty považujeme za příkaz. Potom v rámci lokálního bloku lze tyto proměnné či konstanty používat až do konce tohoto bloku.

8.2. Podmíněné příkazy

8.2.1. Příkaz if

Podmíněný příkaz if má stejnou syntaxi jako v ostatních programovacích jazycích. Pro ty z vás, kteří se s programováním přece jen nesetkali, uvedeme jeho popis.

Příkaz if má dvě podoby - úplné if a neúplné if.

Úplné if je tvořeno:

[ukázka kódu]
if (podminka) { ... } // provede se telo prikazu {}, pokud podminka plati (ma hodnotu true) 
else { ... } // provede se telo prikazu {}, pokud podminka neplati (false)

Neúplné if je tvořeno podobně jako předchozí úplné if, s tím rozdílem, že neuvedeme situaci, kdy podmínka neplatí:

[ukázka kódu]
if (podminka) { ... } // provede se telo prikazu {}, pokud podminka plati

Místo bloku (složeného příkazu) lze použít jednoduchý příkaz. V tom případě za příkazem musíme napsat i středník.

Příkaz if (jak úplný, tak i neúplný) můžeme také vnořit do předchozího příkazu if a vytvářet tak složité konstrukce na testování i různých podmínek. Objeví se zde ale problém v přehlednosti kódu. Proto je výhodné psát program co možná nejlépe strukturovaně, odsazovat jednotlivé části kódu, bloky, abychom se v nich vyznali buď my nebo ti, kteří po nás kód budou číst. V prostředí Microsoft Visual Studio .Net se o odsazování většinou starat nemusíme, nicméně někdy se stane, že vkládáme část kódu (např. pomocí Copy-Paste), která se nezformátuje správně.

8.2.2. Příkaz switch

Příkaz if je přehledný, pokud testujeme dvě podmínky. Pak se stává nepřehledným. Příkaz switch používáme pro testování více vstupních podmínek. Jeho zápis je ve tvaru:

[ukázka kódu]
switch (vyraz) {
   case hodnota1 : 
      prikazy;
   break;
   case hodnota2 :
      prikazy;
   break;
   ...
   default : prikazy;
}

kde vyraz značí výraz, který chceme testovat. Musí nabývat pouze hodnot celočíselných, char, výčtových nebo string. case hodnota1, case hodnota2... jsou návěští, pro které chceme, aby příkaz switch reagoval a default je návěští, kam se switch dostane, pokud se mezi konstantami hodnota1, hodnota2... nenajde ani jedna, která by odpovídala hodnotě výrazu. Návěští default není povinné, takže pokud switch neobsahuje návěští default a nenajde konstantu, která by odpovídala hodnotě výrazu, neprovede se nic.

8.3. Cykly

Cykly také nazýváme smyčkami nebo iteračními příkazy. Jedná se o příkazy, které provádějí cyklicky jeden nebo několik příkazů. Většinou počet opakování závisí na podmínce cyklu (opakování).

8.3.1. Příkaz while

[ukázka kódu]
while (podminka) { ... }

Cyklus while se provádí tak dlouho, dokud podmínka podminka nabývá hodnoty true. Pokud podmínka nabude hodnoty false, cyklus skončí (tělo cyklu se přeskočí). U toho cyklu se může stát, že se neprovede ani jednou, neboť pomínka se vyhodnocuje před tělem cyklu.

8.3.2. Příkaz do-while

Cyklus do-while je podobný jako cyklus while s tím rozdílem, že podmínka se vyhodnocuje až po průchodu těla cyklu. Tedy tělo cyklu se provede pokaždé alespoň jednou.

8.3.3. Příkaz for

Příkaz for se používá asi nejčastěji z příkazů pro cykly. Je užitečný jak pro procházení indexů pole nebo jednoduchého opakování na základě změny hodnoty proměnné. Nepoužívá se ale nutně pouze v těchto případech.

[ukázka kódu]
for(promenna;podminka;zmena_promenne) { ... }

Proměnná promenna určuje počáteční hodnotu proměnné v cyklu. Proměnná může být v tomto místě i deklarována a inicializována. Podmínka omezuje proměnnou, tedy do jaké hodnoty proměnné se má ještě tělo cyklu vykonávat. Jako poslední se definuje, na jakém základě se bude proměnná měnit (zmena_promenne). Následující příklad bude vypisovat na obrazovku hodnotu proměnné od 0 do 9 pokaždé na nový řádek:

[ukázka kódu]
for(int i = 0; i < 10; i++) System.Console.WriteLine(i);

Cyklus for ale může fungovat i bez udání proměnné, podmínky a změny proměnné. Poté se tento cyklus bude provádět donekonečna. Aby se takto vytvořený cyklus dal nějak zastavit, pak se používají (kromě CTRL+C již za běhu programu) příkazy break, return, goto v jeho těle.

8.3.4. Příkaz foreach

Představme si nějaký kontejner (nyní nemám na mysli velkou nádobu, do které se vyhazuje odpad), který obsahuje různé objekty. My chceme pro každý z objektů zjistit typ tohoto objektu. Samozřejmě to můžeme provést několika způsoby, například příkazem for, ale zde se dobře hodí příkaz foreach, který prochází všechny prvky kontejneru:

[ukázka kódu]
using System;
using System.Collections;
...
ArrayList a = new ArrayList();

a.Add(1);
a.Add("string");
a.Add(new object);

foreach(object obj in pole)
   Console.WriteLine(obj.GetType());
...

Program nám následně vypíše:

System.Int32 
System.String 
System.Object

8.4. Skokové příkazy

Příkazy, které způsobí, že program přeruší přirozenou posloupnost plnění příkazů (tedy za sebou, jak jsou napsány), se nazývají skokové příkazy. Tehdy program přejde na jiné místo v programu rozdílné od této přirozené posloupnosti.

8.4.1. Příkaz break

Příkaz break způsobí, že program vyskočí z nejbližšího cyklu, ve kterém se právě nachází. Kromě cyklů se break smí použít i v příkazu switch, jak jsme uvedli výše. V cyklu se používá následovně:

[ukázka kódu]
using System.Collections;
...
ArrayList a = new ArrayList();
a.Add(3);
a.Add("string");
a.Add(new object());
foreach(object o in a)
   if(o.GetType().Name == "String") break;
...

v cyklu foreach postupně zjištujeme, zda typ objektu je instancí třídy String. Pokud ano, příkaz break způsobí ukončení cyklu. Zde použitá metoda GetType() obvykle vrací kromě názvu typu také jmenný prostor (popřípadě prostory, pokud je vnořený), kam typ náleží. Vlastnost Name nám vrátí pouze název typu, což je výhodné. Jinak bychom museli zjištovat, v kterém jmenném prostoru se daný typ nachází.

8.4.2. Příkaz continue

Lze použít pouze v cyklech. Pokud použijeme tento příkaz, způsobí to, že přeskočíme zbytek těla cyklu a začneme novou iteraci (další opakování cyklu).

[ukázka kódu]
int sumaSude = 0;

for(int i=0; i<100; i++) {
   if(i % 2 != 0) continue;
   else sumaSude += i;
}

Pokud je i liché číslo, cyklus začne novou iteraci. Pokud je i sudé, přičte se k proměnné sumaSude.

8.4.3. Příkaz goto

Tento příkaz se většinou v novějších programovacích jazycích nesetkával s oblibou. Platilo, že čím více příkazů goto, tím horší je kvalita programu. Jazyk C# tento příkaz obsahuje, ale snaží se zabránit jeho zneužití, např. nelze skočit dovnitř blokového příkazu. Tento příkaz je doporučován pro použití v příkaze switch nebo pro předání řízení do vnějšího bloku, ale ostatní použití není nepovolené.

[ukázka kódu]
...
// tato konstrukce neni povolena
goto UVNITR_CYKLU;
   
for(int i=0; i<100; i++) {
UVNITR_CYKLU :
   System.Console.WriteLine(i);
}
...
// takto prikaz goto pouzit lze
for(int i=0; i<100; i++) {
   if(i == 34) goto VEN;
   else ...
}

VEN : ;
...

8.4.4. Příkaz return

Tímto příkazem se vrací řízení volající funkci. Příkaz return může být spojen i s hodnotou, pokud metoda, která return použije, má návratovou hodnotu jinou než void.

[ukázka kódu]
...
long mocnina(int zaklad, int mocnina) {
   long vysledek = 1;

   for(int i = 1; i <= mocnina; i++)
      vysledek *= zaklad;
   
   return vysledek;
}
...

8.4.5. Příkazy checked a unchecked

Pokud používáme konverzi typů, někdy se stane, že při konverzi z jednoho typu na druhý si chceme být jisti, že převod proběhl úspěšně. C# pro ten případ používá příkaz checked. Když potom chceme ověřit správnost konverze, uvedeme naši konverzi v kontrolovaném bloku příkazu checked.

[ukázka kódu]
uint iSmall = 3;
byte bSmall;
uint i = 412;
byte b = 0;
...
// standardne je kontrola nastavena jako unchecked
bSmall = (byte)iSmall;
b = (byte)i;
System.Console.WriteLine(b+", "+bSmall);

// nic se nemeni
bSmall = unchecked((byte)iSmall);
b = unchecked((byte)i);
System.Console.WriteLine(b+", "+bSmall);

// zatim vse v poradku
bSmall = unchecked((byte)iSmall);
// zde pri konverzi vyskoci vyjimka
b = checked((byte)i);
System.Console.WriteLine(b+", "+bSmall);

Jelikož kontrola správnosti konverze má určitý vliv na výkonnost, nedoporučuje se ji používat u široce distribuovaného softwaru.

Je to ale užitečný pomocník pro kontrolu, zda dané přetypování proběhlo úspěšně. Proto se dá použít při kompilaci přepínač /checked, který způsobí, že všechny konverze typů se budou kontrolovat. Poté je ale třeba všechny konverze, které kontrolovat nechceme, označit pomocí příkazu unchecked.

8.4.6. Příkaz throw

Příkaz throw slouží k vyvolání výjimky. Uplatní se tehdy, pokud chceme ošetřit nějakou část kódu. V případě, kdy se vyskytnou jiné podmínky v programu, než jsme si představovali, můžeme pomocí throw vyvolat nějakou výjimku. Příklad:

[ukázka kódu]
...
// deleni dvou realnych cisel
double vydel(double delenec, double delitel) {
   if(delitel == 0) throw new DivideByZeroException("Nulou nelze delit!");
   else return(delenec / delitel);
}
...