C# esetén minden objektum az Object osztályból származik. A közös Å‘s tÃpus elÅ‘nye, hogy ezen tÃpus felhasználásával tudunk olyan metódusokat, kódrészleteket Ãrni, amelyek univerzálisak, vagyis tÃpus megkötés nélkül működnek. Ez azért lehetséges, mivel a keretrendszer és a C# fordÃtó egy ilyen metódus hÃváskor képes átalakÃtani egy adott érték tÃpust Object tÃpusra. Ezt a folyamatot nevezzük boxing-nak, magyarul dobozolásnak.
int x = 33;
object o = x; //boxing
Ahogy a példán látható, a dobozolás folyamata implicit, külön jelzést nem igényel. A kódrészletben x tÃpusa egész szám. Mivel minden tÃpusnak az Å‘se object, ezért x konvertálható az Å‘sévé, vagyis egy sima object objektummá. A boxing folyamat közben x tulajdonságai ugyanúgy megmaradnak, csak egy speciális memóriaterületen tárolódnak.
Az unboxing folyamat, az, amikor egy dobozolt értéket konvertálunk vissza az eredeti tÃpusára. Ez a folyamat explicit jelzést kÃván, mivel a dobozolás folyamatában keletkezik egy object és egy speciális memóriaterületen az eredeti tÃpus tulajdonságai, amiket hozzáadott az object tÃpushoz. Viszont az object tÃpusban nem tárolódik semmi extra információ arról, hogy mi is volt az eredeti tÃpus, ezért nekünk kell ezt a kódban jeleznünk.
A fenti példát folytatva:
int szam = (int)o; //unboxing
A folyamat során a korábban leválasztott tulajdonságok és az object objektum kombinálódnak vissza az eredeti tÃpusra. Az eredeti tÃpust észben kell tartanunk, mivel ha rossz tÃpust adunk meg, akkor a konverzió nem fog sikerülni.
var szam2 = (short)o; //hibát fog eredményezni
//jelen esetben kivédhető a Convert osztály használatával
A hiba futás idÅ‘ben fog jelentkezni InvalidCastException formában, mivel egy nem kompatibilis tÃpus konverziót próbálunk kikényszerÃteni.
Ha ezt el szeretnénk kerülni és biztonságosan szerenténk tÃpusok között konvertálni, akkor használhatjuk az as kulcsszót. Ez attól lesz biztonságos, hogy ha a konverzió nem sikerül, akkor az as konverzió eredménye null lesz, amit lekezelhetünk.
Fontos megjegyezni, hogy ez a módszer csak referencia és nullable tÃpussal működik, illetve generikusoknál csak akkor, ha van megkötésünk a tÃpusra (errÅ‘l majd késÅ‘bb részletesen lesz szó).
A használata rendkÃvül egyszerű:
var a = b as T
if (a==null)
{
// a cast nem sikerült
}
else
{
//az a változó T tÃpusú
}
A kód egyébként egyenértékű azzal, mintha azt Ãrnám, hogy
var a = b is T? (T)b : (T)null
Igyekezzünk az as kulcsszóval konvertálást csak akkor alkalmazni, ha mindenképpen szükségünk van a futás idejű konverzióra és nem tudjuk a konverziónkat fordÃtási idÅ‘ben kiváltani egy explicit konverzióval. Az explicit konverzió minden esetben gyorsabb, mint egy as hÃvás, mivel az as operátor futás elÅ‘tt még tÃpust is ellenÅ‘riz.
Az alábbi kódrészlet a boxing és unboxing lehetséges hibáinak kezelését mutatja be:
using System;
namespace PeldaBoxingAs
{
class Program
{
static void Main(string[] args)
{
int ertek = 11;
object boxed = ertek;
if (boxed is int)
{
//explicit unbox
int unboxed = (int)boxed;
Console.WriteLine("A csomagolt adat int. Erteke: {0}", unboxed);
}
try
{
//mivel úgyis hibát dob
short short_unbox = (short)boxed;
//ez nem fog lefutni
Console.WriteLine(short_unbox);
}
catch (InvalidCastException)
{
//nem szép megoldás, igyekezzünk ne Ãgy használni.
Console.WriteLine("Konvertálási hiba");
}
//csak referencia tÃpusok és nullable esetén
//használható az as operátor!
short? short_unbox2 = boxed as short?;
if (short_unbox2 == null)
{
Console.WriteLine("Short-ra nem sikerült konvertálni");
int? un_int = boxed as int?;
Console.WriteLine("A csomagolt adat int. Erteke: {0}", (int)un_int);
}
Console.ReadLine();
}
}
}
A program kimenete:
A csomagolt adat int. Erteke: 11
Konvertálási hiba
Short-ra nem sikerült konvertálni
A csomagolt adat int. Erteke: 11
A példában az as operátornál látható, hogy ha nullable tÃpusra van átalakÃtva egy érték tÃpus, akkor használható konvertálásra. Valójában a nullable tÃpusra konvertálás egy objektumot hoz létre, amelybe az értéktÃpusunk tagváltozóként ágyazódik be.
A tagváltozó értékét a Value tulajdonságon keresztül is lekérdezhetjük, illetve az ilyen objektumok rendelkeznek egy HasValue tulajdonsággal is, amely nullable bool tÃpusú. Ezen keresztül azt tudjuk lekérdezni, hogy a Value által tárolt érték érvényes-e vagy sem.
A boxing és unboxing a sebességre negatÃv hatással van értéktÃpusok esetén. Mégpedig azért, mert a boxing folyamat során átkerül a stack-en tárolt értéktÃpusunk a heap-re egy referencia tÃpusba (object) dobozolva. Az unboxing során az érték visszakerül a stack-re. Ez a folyamat memória másolással jár, aminek a költsége nem elhanyagolható, fÅ‘leg, ha sokszor történik.