Murphy általános érvényű törvénye a szoftverfejlesztésben sem kerülhető ki: ha valami félremehet, akkor félre is fog menni. Ezt szerencsére maguk a szoftverfejlesztők is tudják, és az idők során változatos módszereket dolgoztak ki arra, hogy ha egy függvény (metódus) hívása során valami hiba lép fel, akkor azt jelezzék a hívó felé.
Ez legtöbbször a 0 érték visszadása volt, vagy éppen, ha sokféle hiba léphetett fel, a 0 volt a jó, és a pozitív számok jelölték a hibakódot, de az is lehetett, hogy a -1 volt a hiba, és a többi a jó. Vagy fordítva.
Vagy lehetett bármi más magic numbert visszaadni a hibás végrehajtódást jelzendő - később, ahogy a számítógép-architektúrák fejlődtek, és az int egyre szélesebb lett, egyre tágabb tér nyílt egyre mágikusabb számok használatára.
Akkoriban a szoftverfejlesztők még acélos férfiemberek voltak, akik ezeket a számokat mind fejből tudták - legalábbis a visszaemlékezések szerint, valójában rengeteg időt (=pénzt) emésztett fel, hogy kiderítsék, hogy aznap épp mi jelenti a jót és mi a rosszat.
Nem csoda, ha ma ilyen módszerrel már csak Chuck Norris dolgozik - az objektum-orientált személet ebben is nagy váltást hozott. Az OOP-s srácok lényegesen optimistábbak voltak, és azt mondták, hogy hiba, az csak kivételes esetben fordulhat elő, és gyorsan kitalálták a kivétel-kezelést, ami ma már - apróbb különbségekkel ugyan, de - minden OOP nyelvben megtalálható. A kivétel-kezelés jó, a kivétel-kezelés szép. Két egyszerű ökölszabály:
- ha hibába ütközünk, dobjunk exception-t
- ha olyat hívunk, ami kivételt válthat ki, zárjuk try-catch blokkba
Hogy mi? Vegyük például a következő kódrészletet:
Stream stream = MyFileManager.OpenFile("TheresNoSuchFile.txt"); using (StreamReader streamReader = new StreamReader(stream)) { Console.WriteLine(streamReader.ReadToEnd()); }Egyszerű, megnyitjuk egy fájlt a saját FileManager-ünk segítségével, és végigolvassuk. De mi a helyzet, ha a megnyitni szándékozott fájl nem létezik?
Amennyiben erőforrás-takarékos módon egy null-t adtunk vissza, akkor kapunk egy közönséges NullReferenceException-t. Ez legalább annyira közönséges, mint a közönséges levesteknős, vagy a közönséges húsleves.
Egy biztos: túl sok mindenre nem fog fény derülni a kivételből, jótanácsként is maximum azt kapjuk, hogy használjuk a "new" kulcsszót új objektumpéldány létrehozásához.
Ha úgy érezzük, hogy azon már túl vagyunk, hogy a new operátorral ismerkedjünk, szálljunk magunkba, és nézzük meg mit csinálhatnánk jobban. Dobhatnánk pl. egy FileNotFoundException-t a remek fájlmegnyitó-függvényünkben, egy jól fejlett message-el, amiben elmagyarázhatjuk magunknak, hogy mit nem és miért nem. Hát nem szebb?
Persze van, amikor tényleg overkill az exception dobás, pl. egy statikus gyártófüggvényben lehet jobb megoldás a return null; (főleg, ha esetleg pont azért hoztunk létre, hogy ne a konstruktorból kelljen hányni a kivételt). Ez egyedi elbírálás kérdése, de jól gondoljuk meg, nem csinálunk-e magányos Chuck Norrist magunkból, csak hogy öt sor kóddal kevesebbet írhassunk.
Aki azt gondolná, ilyet csak amatőrök követnek el: ezt a hibát konkrétan a Microsoft is elkövette a .NET Framework 1.x verziójában (a 2.0 után javították). Ha az assemblynkbe nem beágyaztnuk, hanem hozzácsatoltunk egy erőforrást - azaz az mezei fájlként ott figyelt a .dll mellett, és szükség esetén a futtatókörnyezet megnyitotta és berántotta - pont ez volt a helyzet. Ha tényleg ott volt a fájl, semmi gond, de ha véletlenül (gonoszak direkt) letöröltük, akkor az 1.1 egy szép NullReference-szel örvendeztetett meg, és lehetett pislogni, hogy mi van. A 2.0 megmondja: nincs meg ez a fájl, öreg, nem lesz ez így jó.
A témára egyébként Richard Grimes Fusion Workshopjában bukkantam (egészen pontosan itt), ami most tök offtopic - a Fusion-ről, azaz a .NET assembly betöltő mechanizmusáról szól -, viszont szintén nagyon érdekes, érdemes elolvasni.

2 megjegyzés: