Személyes eszközök
Keresés

 

A InfoWiki wikiből

Többirányú elágazás

Gyakran adódik olyan lehetőség, amikor egy változóban szereplő egész érték (vagy egy kifejezés, amely egész értéket eredményez) függvényében kell a többirányú elágazást végrehajtani. Korábban szerepelt a példa az érdemjegy (1..5 közötti valamely érték) alapján történő szelekcióra, amelyet 5 darab if és else kombinációjaként sikerrel oldottunk meg:

Console.WriteLine("Írd be az érdemjegyed:");
 int a = int.Parse(Console.ReadLine());
 if (a==1) Console.WriteLine("elégtelen");
 else if (a==2) Console.WriteLine("elégséges");
 else if (a==3) Console.WriteLine("közepes");
 else if (a==4) Console.WriteLine("jó");
 else if (a==5) Console.WriteLine("jeles");
 else Console.WriteLine("nana, viccelsz, ugye?!");

Ez esetben a fenti kód helyett egy célszerűbb, egyszerűbben módosíthatóbb módszert szoktunk alkalmazni, a switch és case kulcsszavak segítségével. A switch jelentése szétválasztás (esetünkben), a case szó jelentése eset:

Console.WriteLine("Írd be az érdemjegyed:");
 int a = int.Parse(Console.ReadLine());
 switch (a)
 {
   case 1: // ha a==1
     Console.WriteLine("elégtelen");
     break;
   case 2: 
     Console.WriteLine("elégséges");
     break;
   case 3: 
     Console.WriteLine("közepes");
     break;
   case 4:
     Console.WriteLine("jó");
     break;
   case 5:
     Console.WriteLine("jeles");
     break;
   default: // különben
     Console.WriteLine("nana, viccelsz, ugye?!");
     break;
 }

Ez esetben az a változóban lévő érték alapján történik a szelektálás (ez szerepel a switch utáni zárójelpárban). A case után írt literál értékével kerül az a értéke összehasonlításra. Ez alapján van a==1 esetünk, amikor is az elégtelen kerül kiírásra. A switch abban is különleges, hogy az egyes esetekhez tartozó utasításokat nem blokkjelek közé kell zárni, mint az if vagy else esetén, hanem a nélkül is használható több utasítás. Ennek oka, hogy a switch-nek magának is van egy hatalmas, átfogó blokkja, melyben az esetek (case) csak a nagy, hosszú utasítássorozat belépési pontjai. Ennek megfelelően minden egyes ág (case) végére egy break utasítás is kerül, melynek értelme, hogy a switch egyetlen hosszú utasítássorozatának végrehajtása ezen a ponton törjön meg (break=megtörik), szakadjon meg, és folytatódjon a program végrehajtása a switch utáni következő utasításon.

A break ennek megfelelően egyfajta ugró utasítás, jelentését értelmezhetjük úgy is, hogy ugorj ki a switch-ből a következő utasításra. A break használata a C# nyelvben kötelező, bár más, hasonló működésű utasítással kiváltható (return, goto, continue), melyekről később lesz szó. A C nyelv egyes változataiban azonban ennek használata nem kötelező (magában az alap C nyelvben sem). Így az alábbi kód szintaktikailag helyes:

Console.WriteLine("Írd be az érdemjegyed:");
 int a = int.Parse(Console.ReadLine());
 switch (a)
 {
   case 1: // ha a==1
     Console.WriteLine("elégtelen");
   case 2: 
     Console.WriteLine("elégséges");
   case 3: 
     Console.WriteLine("közepes");
   case 4:
     Console.WriteLine("jó");
   case 5:
     Console.WriteLine("jeles");
 }

A fenti (C nyelven helyes kód) esetén derül csak ki, hogy az esetek (case) csak belépési pontok. Ekkor ha a==3, akkor a case 3: ponton kezdődik a végrehajtás, kiíródik hogy "közepes", majd break hiányában folytatódik a végrehajtás a "jó", majd "jeles" kiírásával, mikor is a végrehajtás elérné a switch végét, és folytatódna tovább a program.

A break hiánya tehát egy hibázási lehetőség, és a programozók sajnos ebbe a hibába gyakran bele is estek. Mivel a kód szintaktikailag helyes, a fordítóprogram sem jelzi ki még figyelmeztetés (warning) szintjén sem annak hiányát. Persze ezt akár fel is lehet használni arra, hogy rövidítsünk a kódon. Tegyük fel, hogy számunkra a 1..3 érdemjegyek elfogadhatatlanok, csak a 4..5 jegyeknek tudunk örülni:

Console.WriteLine("Írd be az érdemjegyed:");
 int a = int.Parse(Console.ReadLine());
 switch (a)
 {
   case 1:
   case 2: 
   case 3: 
     Console.WriteLine("elfogadhatatlan");
     break;
   case 4:
   case 5:
     Console.WriteLine("örvendezünk vala");
     break;
 }

Ez esetben az első három case esetben is az "elfogadhatatlan" kiírásra lépne a program, majd az őt követő break miatt az örvendezés elmarad. Amennyiben a==4 vagy 5, úgy örvendezünk. Ez a lehetőség pótolja azt a hiányosságot, hogy a case eseteknél mindíg csak egyetlen eset adható meg, intervallum nem. Pl. milyen hasznos lenne ezt így írni:

switch (a)
 {
   case 1..3: // ez nem helyes amúgy!!!
     Console.WriteLine("elfogadhatatlan");
     break;
   case 4..5: // ez nem helyes amúgy!!!
     Console.WriteLine("örvendezünk vala");
     break;
 }

De sajnos nem lehet! A C nyelvből ezt kifelejtették a tervezők, és azóta sem került bele egyetlen C klónba sem. A C# sem ismeri ezt a szintaktikát, míg a Pascal alapú nyelvek igen (habár ott nem switch a kulcsszó).

Hogyan lehet akkor ezt a viselkedést C#-ban modellezni? Erre való a goto utasítás egyik alakja:

switch (a)
 {
   case 1:
     goto case 3;
   case 2:
     goto case 3;
   case 3:
     Console.WriteLine("elfogadhatatlan");
     break;
   case 4:
     goto case 5;
   case 5: 
     Console.WriteLine("örvendezünk vala");
     break;
 }

A goto case 3 jelentése: ugorj a case 3 belépési pontra, tehát kezeld ezt ugyanúgy, mintha a==3 teljesülne. A goto case így szintén lezárja a szóban forgó ágat, csakúgy mint a break is. A fordítóprogram végül is csak az után érdeklődik, hogy ügyelünk-e arra, nehogy az egyik ágról a vezérlés átcsorogjon egy másik esetre. Ez neki megfelel, így elfogadja.

Jegyezzük meg, hogy a goto case előtt állhat utasítás is:


switch (a)
 {
   case 1:
     Console.Write("elégtelen, így ...");
     goto case 3;
   case 2:
     Console.Write("elégséges, így ...");
     goto case 3;
   case 3:
     Console.WriteLine("elfogadhatatlan");
     break;
   case 4:
     Console.Write("jó, így ...");
     goto case 5;
   case 5: 
     Console.WriteLine("örvendezünk vala");
     break;
 }

Ez esetben pl a==2 esetben kiíródik az "elégséges, így... elfogadthatatlan" szöveg. Figyeljük meg, hogy a a==3 részhez nem írtunk "közepes, így..." kiírást, ugyanis gondolkodjunk el rajta, mi íródna ki ekkor pl. az a==2 esetben?!

switch (a)
 {
   case 1:
     Console.Write("elégtelen, így ...");
     goto case 3;
   case 2:
     Console.Write("elégséges, így ...");
     goto case 3;
   case 3:
     Console.Write("közepes, így ...");
     Console.WriteLine("elfogadhatatlan");
     break;
   case 4:
     Console.Write("jó, így ...");
     goto case 5;
   case 5: 
     Console.WriteLine("örvendezünk vala");
     break;
 }

Hogyan lehetne ezt az esetet kezelni oly módon, hogy a hármas és ötös érdemjegy is hasonlóan legyen felépítve, mint a többi? Két megoldás is kínálkozik:

switch (a)
 {
   case 1:
     Console.Write("elégtelen, így ...");
     goto case 3;
   case 2:
     Console.Write("elégséges, így ...");
     goto case 3;
   case 3:
     if (a==3) Console.Write("közepes, így ...");
     Console.WriteLine("elfogadhatatlan");
     break;
   case 4:
     Console.Write("jó, így ...");
     goto case 5;
   case 5: 
     if (a==5) Console.Write("jeles, így ...");
     Console.WriteLine("örvendezünk vala");
     break;
 }

Ebben az esetben a case 5-höz nem csak akkor kerülhet a vezérlés, ha az a==5, így ezt külön vizsgáljuk meg egy if segítségével. Másik megoldás szerint:


switch (a)
 {
   case 1:
     Console.Write("elégtelen, így ...");
     goto case 333;
   case 2:
     Console.Write("elégséges, így ...");
     goto case 333;
   case 3:
     Console.Write("közepes, így ...");
     goto case 333;
   case 4:
     Console.Write("jó, így ...");
     goto case 555;
   case 5: 
     Console.Write("jeles, így ...");
     goto case 555;
   case 333:
     Console.WriteLine("elfogadhatatlan");
     break;
   case 555:
     Console.WriteLine("örvendezünk vala");
     break;
 }

Ez a megoldás szebben néz ki, de van vele egy nagy gond! Csak gondoljuk végig, mi történne, ha a program kezelője érdemjegy gyanánt a 333 vagy 555 értéket írná be!

Egyik ág sem jó

Mi történik akkor, ha egyik eset sem teljesül? Az előző példát vizsgálva mi történik, ha érdemjegyként pl. a 12 érték kerül beírásra? Egyik eset (case) sem tartalmazza a 12-t. Mivel egyik case sem kerülhet kiválasztásra, így egyszerűen egyik ágon sem lép be a végrehajtás a switch belsejében lévő blokkba, ezért a program futása egyszerűen folytatódik a switch után következő utasításon.

Ha egyik ág sem kerül kiválasztásra, az nem okoz futási hibát. Szintaktikai hibát semmiképp, hiszen a szintaktikai hibás switch le sem fordult volna. Ha egyik case ág sem teljesül, akkor a program végrehajtása folytatódik a switch után.


A kifejezés típusa - int

A switch után nem csak változót írhatunk, hanem kifejezést is. A kifejezés típusa string vagy int lehet (a string lehetőségről később lesz szó). Az int esetén -2,147,483,648 .. 2,147,483,647 között lehet ezen kifejezés (vagy változó) értéke. Ennyi esetet nyilván nem fogunk leírni, ez több mint 4 milliárd eset lenne. De mit tehetünk, ha csak néhány (mondjuk 5 db) esetünk van, minden más eset hibásnak minősül?

A default kulcsszó segítségével speciális esetet, ágat írhatunk le:

switch (a)
 {
   case 1: // ha a==1
     Console.WriteLine("elégtelen");
     break;
   case 2: 
     Console.WriteLine("elégséges");
     break;
   case 3: 
     Console.WriteLine("közepes");
     break;
   case 4:
     Console.WriteLine("jó");
     break;
   case 5:
     Console.WriteLine("jeles");
     break;
   default: // különben
     Console.WriteLine("nana, viccelsz, ugye?!");
     break;
 }

A default ág mindíg az utolsó ág a switch-n belül. Akkor kerül kiválasztásra, ha semelyik másik ág nem kerül kiválasztásra. A fenti példában akkor, ha az a értéke sem 1, sem 2, sem 3,4,5. Minden más tekintetben ez egy teljesen ugyanolyan ág - ezért bár utolsó, akkor is le kell zárni break-el vagy egyéb módon.


A kifejezés típusa - string

Amennyiben a switch kifejezése (vagy változójának) értéke egy string, a switch gyakorlatilag ugyanúgy működik ez esetben is. Arra kell csak ügyelni, hogy a string-ek esetén pontos egyezés szükséges (mint minden string egyenlőségvizsgálat esetén):


bool elmult;
 Console.Write("Elmúltál 18 éves:");
 string s = Console.ReadLine();
 switch (s)
 {
   case "igen":
     elmult=true;
     break;
   case "Igen": 
     elmult=true;
     break;
   case "IGEN": 
     elmult=true;
     break;
   case "yes": 
     elmult=true;
     break;
   default: // különben
     elmult=false;
     break;
 }

A fenti kódban azért kellett külön eseteket írni az igen, Igen, IGEN szavakra, mert ezek a kis-nagybetű különbségek miatt más-más stringeknek minősülnek. Ezen persze lehet egyszerűsíteni, ha pl. a stringet összehasonlítás előtt kisbetűs (vagy nagybetűs) alakra hozzuk:

bool elmult;
 Console.Write("Elmúltál 18 éves:");
 string s = Console.ReadLine();
 s = s.ToLower();
 switch (s)
 {
   case "igen":
     goto case "yes";
   case "yes": 
     elmult=true;
     break;
   default: // különben
     elmult=false;
     break;
 }

Amennyiben a ToLower() metódus segítségével a stringet kisbetűs alakra hozzuk, az igen, Igen és IGEN (valamint pl az iGEN) alakok is egyformákká válnak. A yes eset kezelése persze ekkor is külön esetnek minősül. Valamint természetesen az esetek összevonásához itt is használható a goto case forma, természetesen az eset azonosító itt egy string literál kell legyen, vagyis az aposztrófokat kell használni!

continue és return

A break és goto case lezárásokon kívül speciális módon előfordulhat még continue lezárás is - amennyiben a switch egy ciklus belsejében van. Mivel a ciklusokról még nem volt szó, csak előzetesen jegyezzük meg, hogy a continue hatására a ciklus egy újabb végrehajtási kört fog elkezdni, s ily módon a switch ág lezáródik. Ha a switch nincs ciklusban, akkor a continue nem használható!

A return lezárás akkor alkalmazható, ha a switch-ünk valamely alprogramban (függvény) szerepel. Ez gyakorlatilag mindíg igaz, hiszen maga a Main() is egy függvény. A return ez esetben az alprogramot is lezárja, tehát a vezérlés nem csak a switch-ből fog kilépni, hanem a teljes alprogramból. Az alprogramokról később lesz szó, így ezen a ponton a return-el történől lezárást fogadjuk el, mint később tárgyalandó lehetőséget.


Hernyák Zoltán
A lap eredeti címe: „http://wiki.ektf.hu/wiki/Mp1/page250
Nézetek
nincs sb_3.239.109.55 cikk