Személyes eszközök
Keresés

 

A InfoWiki wikiből

(Változatok közti eltérés)
(Felsorolt típus)
(A változó használata)
237. sor: 237. sor:
   Console.WriteLine("höhh???");
   Console.WriteLine("höhh???");
</code>
</code>
 +
 +
Sőt, a switch is kifejezetten szereti ezt az esetet:
 +
 +
<code lang="csharp">
 +
switch(enrico_caruso_hangja)
 +
{
 +
  case hangfekvesek.tenor:
 +
    Console.WriteLine("ez hihető");
 +
break;
 +
  case hangfekvesek.szopran:
 +
    Console.WriteLine("höhh???");
 +
break;
 +
...
 +
}
 +
</code>
 +
 +
== Bekérés billentyűzetről ==
 +
 +
== Kiírás a képernyőre ==
 +
 +
== Konvertálások, típuskényszerítés ==
 +
<alairas>Hernyák Zoltán</alairas>
<alairas>Hernyák Zoltán</alairas>
__NOTOC__
__NOTOC__

A lap 2009. december 17., 08:25-kori változata


A probléma

Az alapvető típusok (int, double, char, bool, stb) a programok és a további típusok alapvető építőkövei. Mivel nyelvi alaptípusok - a program szövegébe épített értékeiket (literálok alakja) a fordítóprogram felismeri.

Gyakori azonban az az eset, amikor ezen típusok által lefedett értékek halmaza túlságosan bő. Tegyük fel például, hogy egy énekkari kórustagok hangjának jellegét kívánjuk (sok más adatuk mellett) programban tárolni. A hangzástípusok csakis a bariton, alt, tenor, mezoszoprán, szoprán értékek közül kerülhet ki. Nincs olyan típus a C#-ban (és más nyelven sem valószínű), amely csakis ilyen értékeket fogad el.

Ilyenkor a szokásos eljárás, hogy az egyes lehetséges értékeket kódoljuk. Pl. bariton lesz a 0, alt az 1, stb. A típus amire szükségünk lesz, valamely egész szám típus, hiszen ebben tudjuk majd tárolni a tényleges értékek kódjait.

Egyrészt vegyük észre, hogy máris kompromisszumot kötöttünk - nem a tényleges értékeket tároljuk, hanem azok kódjait. Ezen még tudunk segíteni valamelyest a konstansok használatával (bár ki fog derülni, hogy nem sokat):

const int bariton = 0;
const int alt = 1;
const int tenor = 2;
const int mezoszopran = 3;
const int szopran = 4;

Ez máris mutatja, hogy amennyiben egy konkrét énekes konkrét énekhangját akarnánk tárolni, akkor az int típust (vagy valamelyik egész szám típust) tudnánk használni:

int enrico_caruso_hangja = tenor;

Vegyük azonban észre, hogy a fenti kód az alábbi módon is írható:

int enrico_caruso_hangja = 2;

Valamint vegyük észre, hogy az alábbi kód sem jelez szintaktikai hibát:

int enrico_caruso_hangja = -42312;

A szóban forgó változó fizikailag képes ezen lehetetlen érték tárolására, a fordítóprogramnak meg nem adtunk semmilyen támpontot hogy ellenőrízhesse, és kizárhassa ezt a logikailag hibás értéket. Szűkíthetnénk a lehetséges értékek halmazát az által pl, hogy nem az int, hanem egy kisebb típust használunk:


byte enrico_caruso_hangja = -42312; // hibás

A byte típus nem enged meg negatív értékeket, ez okozza a fenti kód szintaktikai hibáját, és nem az, hogy nincs ilyen hangfekvés-kód. A fordítónak semmilyen ötlete nincs arról, hogy mik a lehetséges hangfekvés kódok, e miatt a byte esetén is működne az alábbi kód:

byte enrico_caruso_hangja = 178;

Tovább bonyolítja a helyzetet, hogy programunkban más jellegű konstansokat is felvehetnénk:

// termék minőségek
const int megfelelt = 0;
const int turheto = 1;
const int jo = 2;
const int kivalo = 3;

Mivel ezek mind-mind egy egész számot neveznek el, a hangzások kódolása is egész számok segítségével történik, ezért az alábbi kódok nem hibásak a fordító szerint:

byte enrico_caruso_hangja = megfelelt; // gyak. a bariton kódja
if (enrico_caruso_hangja==turheto) {...} // hangja alt ??
while (enrico_caruso_hangja>0) {...} // ebből mi akar lenni??

Tehát ezen kódolásos megoldás fizikailag megoldja a gondjainkat, a programokban ezen a módon végül is tudjuk tárolni az énekesek hanját (ha nem rontjuk el), de a programunkban ezt a dolgot csakis a mi jó szándékunk tartja életben. A fordítóprogram nem segít nekünk ellenőrízni a kódunk helyességét, nem szól, ha hangfekvés jellegű változóinkba illegális értéket akarunk helyezni, és a programunk szövegének utólagos (vagy mások által történő) olvasgatása során a típusinformáció nem ad támpontot a változók szerepének megértésében.

Felsorolt típus

Amire szükségünk lenne, hogy a fordítóprogramnak el tudjuk magyarázni, hogy mire is gondolunk mi. Erre van mód az enum kulcsszó segítségével:

enum hangfekvesek { bariton, alt, tenor, szopran, mezoszopran };

A fenti sorral megadhatjuk, hogy hangfekvesek alatt mi ezt az öt lehetséges értéket értjük a továbbiakban. A fenti kis kódrészben az enum szó foglalt szó, a többi azonban azonosítónak minősül, tehát mind a hangfekvesek szó, mind a bariton, alt stb szavak mindegyike az azonosító névképzési szabályainak megfelelően képzendő (nincs benne szóköz, számjeggyel nem kezdődhet, ékezetes betűk használata elvileg nem tilos, stb).

A fenti kis kódban a hangfekvesek azonosító a továbbiakban egy új típust azonosít, a kapcsos zárójelbeli felsorolás ezen típusba tartozó értékeket ad meg. Ez a konstrukció nagyon hasonlít a matematikai halmaz esetéhez, ahol is a halmazbeli elemeket (véletlenül) szintén a kapcsos zárójelbe szoktuk felsorolni.

Mivel a hangfekvesek itt egy új típus lesz, ezért szabad ilyen típusú változót deklarálni:

hangfekvesek enrico_caruso_hangja;

Ezen típusú változó (mint látni fogjuk) csakis a fenti felsorolásban megadott értékek valamelyikét képes (majd) fölvenni.

Tárolási jellemzők

Hány byte-ba kerül nekünk egy felsorolás alaptípusú változó? A szokásos 4 byte-os tipp megint bejön. Oka: a fenti változó valójában a háttérben int típusú lesz, és egy int mindíg 4 byte.

A háttérbeli kódolást ilyenkor a fordítóprogram végzi el helyettünk, ennek tényét maximálisan elrejtve előlünk. Némi kutakodás és gondolkodás után kideríthető, hogy a fenti kis enum felírás valójában majdnem teljesen egyenértékű a mi kódolási és konstansos kísérletünk eredményével.

Ennek megfelelően ami történik, az nem más, mint 5 darab konstans generálása a fordítóprogrammal, a fenti esetben a felsorolásbeli bariton kódja ténylegesen a nulla lesz, a alt kódja valójában egy, stb.

Kódolás befolyásolása

Néha szükség van arra, hogy ne az automatikus kódkiosztást használjuk, hanem fizikailag adhassuk meg az egyes értékek kódjait. Ezt a felsorolás belsejébe elhelyezett értékmegadásokkal tudjuk befolyásolni:

enum hangfekvesek 
{ bariton=10, 
  alt, 
  tenor=20, 
  mezoszopran,
  szopran=40
};

A fenti felírás eredményeként az alábbi kódolást írtuk elő:

bariton     = 10
alt         = 11
tenor       = 20
mezoszopran = 21
szopran     = 40

Vagyis ahol mi állítottuk be fixen a kódolási értéket, ott az kerül felhasználásra, egyébként pedig az automatikus kódkiosztás során a következő szabad kódot adja a rendszer az egyes lehetséges értékeknek.

A változó használata

Egy ilyen enum változóba tudunk értéket elhelyezni, de csak a lehetésges értékek közül. A lehetséges értékek neveivel tudunk rájuk hivatkozni, mintha ők konstansok lennének. De ezen konstansok minősített konstansok, vagyis nem csak a nevüket kell megadni, hanem azt is, melyik enumbeli melyik értékre gondolunk:

hangfekvesek enrico_caruso_hangja = hangfekvesek.tenor;

Egyes programozási nyelvekben ilyenkor nem kell már használni az enum típusnevét, csak a szóban forgó érték nevét. De ez félreértésekre is okot adhat. Például előfordulhat, hogy a szóban forgó azonosító más enumban is szerepel (és valószínűleg más értékkel):

enum hangfekvesek { bariton, alt, tenor, szopran, mezoszopran };
enum spec_billentyuk {alt, control, shift };

Mi történne ekkor, ha csak az azonosítót adnánk meg:

hangfekvesek kathleen_ferrier_hangja = alt;

Mivel az alt azonosító szerepel a hangfekvesek felsorolásban (1 értéket képviselve), és a spec_billentyuk felsorolásban is (nulla értéket képviselve) átérezhetjük a fordítóprogram zavarát. Ezt tudjuk fokozni, ha bosszantásképpen még definiálunk egy direkte ilyen nevű tényleges (egyszerű) konstanst is:

enum hangfekvesek { bariton, alt, tenor, szopran, mezoszopran };
enum spec_billentyuk {alt, control, shift };
const alt = 322;
...
hangfekvesek kathleen_ferrier_hangja = alt;

Ezért a felsorolással kapcsolatos műveletek esetén mindíg meg kell adni a felsorolás típusának nevét is:

hangfekvesek enrico_caruso_hangja = hangfekvesek.tenor;
hangfekvesek kathleen_ferrier_hangja = hangfekvesek.alt;

A fordítóprogram a típust mindíg nagyon komolyan veszi. Bár a háttérbeli működés szempontjából akár kivitelezhető is lenne, de logikailag ellentmondásos, ezért tiltott pl. az alábbi értékadás:

hangfekvesek tarja_turunen = spec_billentyuk.shift; // hibás

Nem csak értéket lehet benne elhelyezni ilyen direkt módon, az egyforma felsorolás típusú változók közötti értékadás is működik (az értékadás működéséhez a két oldal típusainak megfelelősége szükséges, ami jelen esetben teljesül):

hangfekvesek enrico_caruso_hangja = hangfekvesek.tenor;
hangfekvesek placido_domingo_hangja = enrico_caruso_hangja;

Valamint feltételes elágazásokban is meg lehet vizsgálni a bennük lévő értéket:

if (enrico_caruso_hangja == hangfekvesek.szopran)  
  Console.WriteLine("höhh???");

Sőt, a switch is kifejezetten szereti ezt az esetet:

switch(enrico_caruso_hangja)
{
  case hangfekvesek.tenor:
    Console.WriteLine("ez hihető");
	break;
  case hangfekvesek.szopran:
    Console.WriteLine("höhh???");
	break;
...
}

Bekérés billentyűzetről

Kiírás a képernyőre

Konvertálások, típuskényszerítés

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