16. Hibakezelés - kivételkezelés

Default book

Egy program futás közben általában normál  állapotban van. Ha a futás közben kezeletlen hibára fut (például nullával való osztás, olyan fájl megnyitása, amely éppen nem elérhető, stb.), hiba állapotba kerül a program és az operációs rendszer, illetve a futási környezet által nyújtott hibakezelő rutinok a program futását megszakítják és a program nem fog tovább futni, pontosabban a hiba helyétől a program visszatér a hívási listán keresztül a belépési függvényéig  - Main() - és befejezi működését.

Erre azt mondjuk hogy kivétel történt (Exception) a program futásában. Ha nincsen kivételkezelés (Exception handling), akkor a program leáll.

A C#-ban, mint más nyelven gondot kell lehet fordítani arra, hogy a program futása közben előforduló, előre nem látható, külső körülményekből álló hibákat kezeljük. A komolyabb programoknál nem elegendő az, hogy az operációs rendszer ezt majd megoldja, hanem olyan értelmes megoldások kellenek, amelyeket a programozók kézben tartanak és a felhasználókat a hibák esetén normális módon lehet értesíteni.

Hibakezelés hagyományosan

Az alábbiakban egy példán keresztül megmutatom a hibakezelés hagyományos módját.Tegyük fel, hogy egy adatbevitel során egy változót be lehet vinni a billentyűzetről, majd ennek a változónak az értékével osztani akarunk. Hogyan lehet elkerülni, hogy a 0-val való osztás hibát okozzon?

string sszamlalo;
int szamlalo;
string snevezo;
int nevezo;
int eredmeny;

Console.Write("Kérem a számlálót:");
sszamlalo = Console.ReadLine();
szamlalo  = int.Parse(sszamlalo);

Console.Write("Kérem a nevezőt:");
snevezo = Console.ReadLine();
nevezo  = int.Parse(snevezo);

if (nevezo != 0 )
{
   eredmeny = szamlalo / nevezo;
   Console.WriteLine("AZ eredmény: {0}",eredmeny);
}
else
{
   Console.WriteLine("Hiba! A nevező nem lehet 0");
}

​A fenti program még az osztás művelet elvégzése előtt megvizsgálja, hogy a nevezo nulla vagy nem nulla. Ha a nevező nulla, akkor a program kiírja, hogy a nevező nem lehet nulla és folytatja a működését. 

Ilyen és ehhez hasonló módon ki lehet küszöbölni, hogy egy program az előre látható hibákat hogyan kezelje. Nagyon sok esetben azonban nem lehet előre látni minden problémát, sőt kifejezetten a felhasználói interakciókban sok olyan helyzet fordul elő, amikor a felhasználó irracionálisan viselkedik.

Ha egy kis gyereket odaültetünk egy számítógéphez, hogy kezdj valamit a futó programmal, ő minden elképzelhető és elképzelhetetlen hibát produkálni fog. Tehát nagyon nehéz minden hibára felkészülni.

A fenti programban, ha egy nem numerikus szöveget viszünk be a programba, akkor az int.Parse() utasítás hibát okoz és a program leáll, vagyis ezt is kellene kezelni!

Ameddig a programozási nyelvekben nem voltak a hibakezelésre szabványosított eszközök, addig meglehetősen meglehetősen  nehezen lehetett előre felkészülni minden lehetséges hibára, azonban ma már minden elterjedt programozási nyelv tartalmazza azokat a nyelvi elemeket, amelyeket kifejezetten a kivételkezelésre lehet használni.

Kivételkezelés - try{} catch{}

A kivételkezelő utasításoknak az a lényege, hogyha a program futása közben kivétel keletkezik, akkor a program futása ne fejeződjön be automatikusan, hanem értelmes üzenetet, vagy tevékenységet végezve a program fusson tovább és legyen továbbra is használható állapotban. Persze, hogy mit jelent az értelmes üzenet vagy tevékenység annak meghatározása továbbra is a programozó feladata. Erre használható a try{} catch{} utasítás blokk. A fenti kód kivételkezeléssel így írható le:

string sszamlalo;
int szamlalo = 0;
string snevezo;
int nevezo = 0;
int eredmeny;
bool ok = false;
			
while(! ok){
  try{
    Console.Write("Kérem a számlálót:");
    sszamlalo = Console.ReadLine();
    szamlalo  = int.Parse(sszamlalo);
    ok = true;
  }catch{
    Console.WriteLine("A számláló csak numerikus lehet!");
    ok = false;
  }
}

ok = false;
while(!ok){
  try{
    Console.Write("Kérem a nevezőt:");
    snevezo = Console.ReadLine();
    nevezo  = int.Parse(snevezo);
    ok = true;
  }catch{
    Console.WriteLine("A nevező csak numerikus lehet és nem lehet nulla!");
    ok = false;
  }
}
			
try
{
  eredmeny = szamlalo / nevezo;
  Console.WriteLine("A eredmény: {0}",eredmeny);
}
catch
{
  Console.WriteLine("Hiba! A nevező nem lehet 0");
}

Ha összehasonlítjuk az eredeti kóddal, akkor azt látjuk, hogy hasonló hozzá, de mégis más. eleve ez a kód lekezel minden felhasználói hibát. Nem csak a 0-ával való osztás problémáját, hanem azt is, hogyha a felhasználó értelmetlen adatokat visz be a billentyűzetről.

A try ...catch... utasításpár még további lehetőségeket is tartalmaz.

A C#-ban vannak a hibakezelés céljára előre meghatározott hibakezelú objektumok, amelyek az ős hibakezelő leszármazottjai. Az objektumoknak van egy Message része, amelynek segítségével szabványos hibaüzeneteket lehet kiírni a felhasználó részére.