Kivételt dobni jó! (kivéve persze amikor nem az)
Kivételt dobhatunk, bárhol, bármikor - legfejlebb furcsa következményei lesznek. Például ha egy statikus konstruktorból szökik ki egy kezeletlen kivétel, azt nem sok esélyünk lesz elkapni, tekintve, hogy egy osztály statikus konstuktorát nem mi hívjuk, hanem a CLR a típushoz való első hozzáférés előtt valamikor - ő pedig ezt úgy fogja értelmezni, hogy ennek a típusnak az inicializálása nem sikerült túl jól, úgyhogy a jövőben leszünk szívesek tartózkodni a használatától (legalábbis a bűnös AppDomainben).
Kivételt persze dobhatunk nem csak statikus, de "hagyományos" (példány) konstruktorból is. Itt már jobb esélyekkel indulunk, ha ezeket kezelni is szeretnénk, de furcsaságokkal találkozhatunk itt is. Vegyük pl. az alábbi osztályt:
class MyType { public string Name { get; private set; } public MyType(string name) { // Ezzel most egyelőre ne törődjünk: // InstanceLibrary<MyType>.RegisterInstance(name, this); Name = name; if (name.Length > 5) throw new ArgumentException("The name is too long."); } }Eddig semmi extra, egy példány, egy név, a nevet megkapjuk konstruktor paraméterben, illetve egy extra ellenőrzés: ha név hosszabb, mint öt karakter, akkor arra azt mondjuk, hogy na ezt ne, ez már tényleg sok.
Van még egy kikommentezett sorunk, azzal egyelőre ne törődjünk, majd mindjárt meglátjuk mire lesz jó. Vigyázó szemeink vessük inkább az alábbi kódra:
MyType i1 = null, i2 = null, i3 = null; try { i1 = new MyType("Jenő"); i2 = new MyType("Benő"); i3 = new MyType("Szigfrid"); } catch(Exception ex) { Console.WriteLine("An exception has been thrown: {0}", ex.Message); Console.WriteLine("i1={0}", i1 != null ? i1.Name : "[null]"); Console.WriteLine("i2={0}", i2 != null ? i2.Name : "[null]"); Console.WriteLine("i3={0}", i3 != null ? i3.Name : "[null]"); }Mi lesz ebből? Az első két instance szépen létrejön, Szigfrid már kiveri a biztosítékot, de szépen lekezeljük a kivételt, ő (i3) null lesz, az objektum már létre sem jön.
Legalábbis azt gondolhatnók (C++ múlttal rendelkezők akár a nagyesküt is leteszik) - mi egyelőre inkább maradjunk abban, hogy i3 értéke null lesz.
Most nézzük meg a kikommentezett rész. Vegyük az alábbi remek kis példány-tárolót, és vegyük ki a kommentet a MyType konstruktorából.
static class InstanceLibrary<T> { private static Dictionary<string, T> _instacesByName = new Dictionary<string, T>(); public static void RegisterInstance(string name, T instance) { _instacesByName.Add(name, instance); } public static IEnumerable<T> AllInstances { get { return _instacesByName.Values; } } }Majd a fenti try-catch-es konstruálási folyamat után menjünk végig az összes létrehozott példányon:
foreach (var instance in InstanceLibrary<MyType>.AllInstances) { Console.WriteLine(instance.Name); }A már hagyományőrző találós kérdés: hány példány lesz?
Hát persze, hogy három. Sőt, a legjobb, hogy az elvileg nem létező MyType példányunk, Szigfrid, rezzenéstelen arccal közli velünk kivételesen hosszú nevét.
A CLR tehát a konstuktorból dobott kivétel ellenére létrehozott egy példányt, bár a new operátor már nem adott vissza nekünk referenciát erre a félig-meddig megkonstruált instancera - bölcs döntés, hisz az valószínűleg valamilyen inkonzisztens állapotban van, nem lenne szerencsés használni.
De ettől még létezik, bár referencia nincs rá - ideális zsákmányállat a GC számára. Szépen ki is fogja dobni, meg finalizálni, meg amit szokott csinálni az ilyen alakokkal. Ha finalizert is csinálunk az osztályunkhoz, IDisposable-ek vagyunk, nem árt erre is felkészülni (például úgy, hogy ha a konstruktorban tudjuk, hogy baj lesz, már ott hívunk egy GC.SuppressFinalize(this)-t).
Tanulság?
Szintén hagyományőrző jelleggel: nincs. A lehetőségeink határtalanok, de figyeljünk oda mit csinálunk, mert a szoftverfejlesztés - managed vagy nem managed - veszélyes üzem: könnyen lábon lőhetjük magunkat. A lehetőségeink ugyanis e téren is határtalanok.

0 megjegyzés:
Megjegyzés küldése