A InfoWiki wikiből
(Új oldal, tartalma: „<cim cim3="Többirányú elágazás" cim2="Magasszintű Programozási Nyelvek I." cim1="Imperatív, procedurális nyelvek alapjai" prev="mp1/page240" next="mp1/pag...”) |
Aktuális változat (2009. október 15., 09:45) (lapforrás) |
||
11. sor: | 11. sor: | ||
<path p1="Kezdolap|Kezdőlap" p2="EBooks|Jegyzetek" p3="mp1/Nyitolap|Tartalomjegyzék" | <path p1="Kezdolap|Kezdőlap" p2="EBooks|Jegyzetek" p3="mp1/Nyitolap|Tartalomjegyzék" | ||
xx="-" | xx="-" | ||
- | w1="mp2 | + | w1="mp2/Nyitolap|Gyakorlatok" w2="mp1/Tematika|Vizsgatematika" w3="cs/Blog|Programok C# nyelven" |
/> | /> | ||
Aktuális változat
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.
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.