1 Hernyák Zoltán Programozási Nyelvek II. Eszterházy Károly Főiskola Számítástudományi tsz
2 class Verem { public Object Pop() { if (vm>0) { vm--; return t[vm]; } else ??? } class Verem { public Object Pop() { if (vm>0) { vm--; return t[vm]; } else ??? } A fő kérdés: hogyan jelezze ez az eljárás azt, hogy nem lehet már kivenni elemet a veremből, mert a verem üres?
3 Hibajelzés első fontos kérdése: kinek jelezzük a hibát? -közvetlenül a felhasználónak: -Hangjelzéssel (beep) -Üzenet kiírásával (message) -Grafikus felületre (Dialog Box) -Konzolos felületre (Console.WriteLine) -Nyomtatóra -Napló file-ba (log file) írással -Program saját napló file-a -Windows központi napló file
4 class Verem { public Object Pop() { if (vm>0) { vm--; return t[vm]; } else { hibajelzés; return ???; } class Verem { public Object Pop() { if (vm>0) { vm--; return t[vm]; } else { hibajelzés; return ???; } Object x = verem.Pop();... // x feldolgozása Object x = verem.Pop();... // x feldolgozása
5 Hibajelzés első fontos kérdése: kinek jelezzük a hibát? - jeleznünk kell a bennünket meghívó eljárásnak is, hogy hiba történt a végrehajtás közben, a visszaadott érték nem tekinthető feldolgozhatónak…... else { hibajelzés; return null; }... else { hibajelzés; return null; } Object x = verem.Pop(); if (x==null) hiba_történt; else x_feldolgozása; Object x = verem.Pop(); if (x==null) hiba_történt; else x_feldolgozása;
6 Hibajelzés fontos szabálya: egyértelműen kiderüljön, hogy valóban hiba történt! verem.Push( null );... Object x = verem.Pop(); if (x==null) hiba_történt; else x_feldolgozása; verem.Push( null );... Object x = verem.Pop(); if (x==null) hiba_történt; else x_feldolgozása; Jelen esetben a Pop() visszatérési értékéből (hibásan) az következik, hogy hiba történt a Pop futása közben.
7 Nagyon gyakori, hogy a fv-ek int típusú értékkel térnek vissza, ahol az egy hibakódot jelent. Leggyakrabban a 0 jelenti -> nem történt hiba, más értékek a hiba okának kódját jelölik. Ez nagyon rossz módszer, mert nincs szabvány a hibakódokra –> minden fv más-más kódolást használ – megjegyezhetetlen és kibogozhatatlan. Ráadásul, ha a hívó fél nem ír rögtön ‘if’-et a hívás után, akkor hiába adunk vissza hibakódot. Ez a hibajelzés csak újabb és újabb hibák előidézésre jó. Csak nagy odafigyelés mellett használható!
8 Ráadásul konstruktorok esetén nem is használható! ( a konstruktornak nincs visszatérési típusa, értéke ) Ez a fenti sor elvileg megnyitná a megadott file-t olvasásra. De mi van, ha a file nem létezik, vagy nincs jogosultságunk megnyitni? Hogyan jelezzük a hibát? Itt nem megy a hibakód visszaadása… FileStream f = new FileStream(”C:\\hello.txt”);.. file feldogozása az ‘f’ példányon keresztül.. FileStream f = new FileStream(”C:\\hello.txt”);.. file feldogozása az ‘f’ példányon keresztül..
9 Kivétel (Exception): -Ez egy jelzés, amelyet explicit módon indít el (lő fel) a függvény, ha jelezni akarja a hívó fv-nek, hogy hiba történt a végrehajtás közben -A fv a hiba jelzés után azonnal visszatér a hívás helyére (azonnali return) -Ezen visszatérés rendhagyó, a visszatérési érték, és a cím szerinti kimenő paraméterek értéke ekkor nem definiált (e miatt nem is szabad felhasználni) -Ha a hívó ponton a feldobott kivételt nem kezeljük le, akkor ezen fv is azonnal visszatér az őt hívó helyre (hivási lánc visszagörgetése)
10 Kivétel (Exception): -Ha a feldobott kivételt a hivási verem egyik mélységében sem kezeljük le, akkor minden fv visszatér. A végén eljutunk a Main fv szintjére. - Ha a Main() fv sem kezeli le a hibát, akkor a Main fv is visszatér a hívási helyére – ez az operációs rendszer. - Ez gyak. azt jelenti, hogy a program futása befejeződik.
11 class Verem { public Object Pop() { if (vm>0) { vm--; return t[vm]; } else throw new Exception(”Üres”); } class Verem { public Object Pop() { if (vm>0) { vm--; return t[vm]; } else throw new Exception(”Üres”); } A hiba feldobását a ‘throw’ végzi. A throw paramétere egy Exception osztály (vagy vele kompatibilis) példány.
12 Exception e = new Exception(”Üres”); throw e; Exception e = new Exception(”Üres”); throw e; Ritkán szoktunk ilyen kétlépéses példányosítást. Gyakrabban… throw new Exception(”Üres a verem”); A konstruktor egy string-et vár paraméterként. Ebben a string-ben egy hibaüzenet-et specifikálhatunk. Ha senki sem kapja el a hibát, és eljut az operációs rendszerig, akkor ez a hibaüzenet megjelenik egy párbeszéd-ablakban.
13 A System.Exception a legáltalánosabb hiba jelzés. A hiba okát már a példány típusának kiválasztásával is jelezhetjük: System.ArithmeticException System.ArrayTypeMismatchException System.DivideByZeroException System.IndexOutOfRangeException System.InvalidCastException System.NullReferenceException System.OutOfMemoryException System.StackOverflowException System.TypeInitializationException
14 A System.Exception osztályból számos gyerekosztály készült (hiba-hierarchia). Példa: System.Exception System.SystemException System.IO.IOException System.IO.DirectoryNotFoundExceptionSystem.ExceptionSystem.SystemExceptionSystem.IO.IOException System.IO.FileNotFoundException throw new FileNotFoundException(”Nincs meg a konfigurációs file!”);
15 try { … } catch (Exception e ) { … } try { … } catch (Exception e ) { … } Az a programszakasz, amelyik kivételt dobhat Itt specifikáljuk, milyen típusú kivételeket kívánunk elkapni Itt dolgozzuk fel a kivételt ( mit kell csinálni, ha kivétel történt ) A kivétel elkapásához a try…catch blokkot kell használni. Ezen a néven hivatkozhatunk a feldobott példányra a kivételt feldogozó kódban (változó-deklaráció)
16 A Base Class Library metódusaihoz a help-ben le van írva, hogy milyen típusú kivételt dobnak… Exception TypeCondition ArgumentExceptionpath is an empty string (""). ArgumentNullExceptionpath is a null reference (Nothing in Visual Basic). FileNotFoundExceptionThe file cannot be found. DirectoryNotFoundExceptionThe directory cannot be found. IOExceptionpath includes an incorrect or invalid syntax for file name, directory name, or volume label. public StreamReader( string path );string public StreamReader( string path );string
17 try { if (a==0) throw new DivideByZeroException(”az ‘a’ változó értéke 0”); else int c = b/a; … } catch (Exception e ) { if (e is DivideByZeroException) { … } if (e.Message==”az ‘a’ változó értéke 0”) { … } } try { if (a==0) throw new DivideByZeroException(”az ‘a’ változó értéke 0”); else int c = b/a; … } catch (Exception e ) { if (e is DivideByZeroException) { … } if (e.Message==”az ‘a’ változó értéke 0”) { … } } A feldobott kivétel megvizsgálása:
18 try { … throw new DivideByZeroException(”az ‘a’ változó értéke 0”); } catch (DivideByZeroException e ) { … } catch (IOException e ) { … } catch (Exception e ) { … } try { … throw new DivideByZeroException(”az ‘a’ változó értéke 0”); } catch (DivideByZeroException e ) { … } catch (IOException e ) { … } catch (Exception e ) { … } A feldobott kivétel típusa szerinti elágazás: Miért nem kezdhetjük az ‘Exception e’ esettel? Milyen kivételeket kap el az IOException eset?
19 try { … throw new sajatException(”a”); } catch (sajatException e ) { if (e.valtozoNeve==”a”) { … } } try { … throw new sajatException(”a”); } catch (sajatException e ) { if (e.valtozoNeve==”a”) { … } } Saját kivételosztály használata: class sajatException:DivideByZeroException { public string valtozoNeve = String.Empty; public sajatException(string ValtozoNev) :base(”az ”+ValtozoNev+” változó 0 volt!”) { this.valtozoNev = ValtozoNev; } class sajatException:DivideByZeroException { public string valtozoNeve = String.Empty; public sajatException(string ValtozoNev) :base(”az ”+ValtozoNev+” változó 0 volt!”) { this.valtozoNev = ValtozoNev; }
20 try { … } catch ( Exception e ) { …. } try { … } catch ( Exception e ) { …. } Elkaptunk egy kivételt… mit csináljunk ? try { … } catch ( Exception e ) { if ( ??? ) throw e; } try { … } catch ( Exception e ) { if ( ??? ) throw e; } try { … } catch ( Exception e ) { if ( ??? ) throw new Exception(”…”); } try { … } catch ( Exception e ) { if ( ??? ) throw new Exception(”…”); } Elkapjuk – feldolgozzuk … a kivétel megszűnik. Elkapjuk – megvizsgáljuk … a kivételt újra feldobjuk…. Másik kivételt dobunk fel…
21 try { … } catch ( FileNotFoundException e ) { Console.WriteLine(”Nem megfelelő file név!”); } try { … } catch ( FileNotFoundException e ) { Console.WriteLine(”Nem megfelelő file név!”); } Ha a példány bennünket nem is érdekel: try { … } catch ( FileNotFoundException ) { Console.WriteLine(”Nem megfelelő file név!”); } try { … } catch ( FileNotFoundException ) { Console.WriteLine(”Nem megfelelő file név!”); } Hiba: ‘e’ deklaráltuk, de nem használjuk fel! Helyesen…
22 Nagyon gyakori eset: erőforrás lefoglalása (inicializálás) … try { … } catch ( Exception e ) { } erőforrás felszabadítása … throw e; // a hiba továbbdobása, de ez sajnos nem jó itt erőforrás lefoglalása (inicializálás) … try { … } catch ( Exception e ) { } erőforrás felszabadítása … throw e; // a hiba továbbdobása, de ez sajnos nem jó itt erőforrás lefoglalása (inicializálás) … try { … } catch ( Exception e ) { erőforrás felszabadítása … throw e; } erőforrás felszabadítása … erőforrás lefoglalása (inicializálás) … try { … } catch ( Exception e ) { erőforrás felszabadítása … throw e; } erőforrás felszabadítása … Ez meg nem elegáns: 1: nincs ‘e’ változó itt 2: csak akkor kell tovább dobni, ha volt is hiba
23 A megfelelő megoldás: erőforrás lefoglalása (inicializálás) … try { … } finally {erőforrás felszabadítása … } erőforrás lefoglalása (inicializálás) … try { … } finally {erőforrás felszabadítása … } A ‘finally’ ág akkor is végrehajtódik, ha a try szakaszban Exception dobódik fel, és akkor is, ha nem. A ‘finally’ nem szedi le a hibát, a hiba attól feldobva marad, a finally blokkból kilépve újra érvényre jut, és visszatér a hívás helyére.
24 public int Akarmi() { try { … } finally { … } return 0; } public int Akarmi() { try { … } finally { … } return 0; } A ‘return 0’ csak akkor hajtódik végre, ha a try blokkban nem volt exception feldobva! Ha feldobásra került, akkor a finally blokk végrehajtódik, de a ‘return 0’ már megint nem…
25 try { try { … } finally { … } } catch (Exception) { … } try { try { … } finally { … } } catch (Exception) { … } A blokkok egymásba ágyazhatók:
26 try { … } catch { … } try { … } catch { … } A típus nélküli catch ekvivalens a Exception típusú catch-al (de ilyenkor nem lehet hivatkozni a catch blokkon belül a kivétel-példányra) :
27 try { … } catch { … csinálunk valamit … throw; } finally { … } try { … } catch { … csinálunk valamit … throw; } finally { … } Ez a sorrendbe kapcsolás megengedett egymásba ágyazás nélkül is: A try-finally-catch sorrend viszont nem!