Személyes eszközök
Keresés

 

A InfoWiki wikiből



Tartalomjegyzék

Boxing, Unboxing

Mint ismert, a 'reference' típusú változók 4 byte-on helyezkednek el, és egy memóriacímet tárolnak, ahol a hozzájuk tartozó tényleges érték tárolásra került. Ezzel szemben egy 'value' típusú változó annyi byte-ot foglal el, amennyit az adott típus igényel, és ezen a területen közvetlenül tárolódik el a változó aktuális értéke.

Két 'reference' típusú változó közötti értékadás során csak a 4 byte-on tárolt memóriacím másolódik át, így innentől kezdve a két változó ugyanarra az értékre mutat, bármelyiken keresztül az érték módosítható.

Két 'value' típusú változó közötti értékadás során a tárolt érték átmásolódik, de a továbbiakban a két változónak "nincs közük" egymáshoz, bármelyik értékének módosítása semmilyen kihatással nincs a másik változó értékére.

Az is ismert, hogy az 'Object' típus minden más típus ősosztályának minősül, így minden más típusú érték elhelyezhető egy 'Object' típusú változóban, és megfelelő típuskonverzió révén onnan vissza is olvasható.

Az is ismert ,hogy az 'Object' típus mint olyan, 'reference' típuskategóriájú.

A fentiek ismeretében hogyan kell értelmezni az alábbi 'o=d' értékadó utasítást?

double d = 12.4;
Object o = d;

Az biztosan kijelenthető, hogy az 'o=d' értékadó utasítás típushelyes, hiszen a jobboldali típus (double) kompatibilis a bal oldali (Object) típussal. Azonban az 'a' változó referencia-típusú, vagyis egy memóriacímet lehet beletölteni, de a jobb oldalon nem memóriacím áll, hanem egy lebegőpontos számérték.

Boxing

Amikor egy referencia-típusú változóba érték típusú (value type) értéket kívánunk elhelyezni, akkor a háttérben egy automatikus előkészítő lépés hajtódik végre, melyet 'boxing'-nak, "becsomagolásnak" nevezünk.

Ennek során a memóriában kialakításra kerül egy megfelelően nagy méretű memóriaterület, melybe átmásolódik az érték (duplikálódik), majd ennek a memóriacíme kerül be a referencia típusú változóba:

Kép:kep.Boxing.png

Ez persze azt is jelenti, hogy a 'o=d' értékadás elvégzése után a 'd' változó értéke szabadon módosítható, az 'o' változóban tárolt '12.4' értéket ez már nem fogja érinteni.

Ez a művelet automatikusan (implicit) lezajlik. Ismerete azért is fontos, mert egyrészt plusz memóriát köt le, másrészt időbe is kerül (memória-másolás), vagyis mindenképpen "költséges" művelet.

Unboxing

Sejthető, hogy a fenti műveletnek van ellentételes párja is:

double d = 12.4;
Object o = d;
...
double c = (double)o;

A 'c=(double)o' értékadás a fenti esetben típushelyes, hiszen az 'o' változóban most éppen egy 'double' típusú érték van tárolva. A típuskonverzióra azért van szükség, mert e nélkül a jobb oldal típusa (Object) nem lenne kompatibilis a bal oldal típusával (double).

Mi történik ennek hatására. Nyilván nem az, hogy az 'o' változó által tárolt memóriacímet mint egy lebegőpontos számot fogja fel a rendszer, és helyezi el a 'c' változóba. Sokkal inkább az 'o' által mutatott memóriaterületen lévő 'double' érték visszanyeréséről van szó.

Alaposabban megvizsgálva a fenti értékadást, azt láthatjuk, hogy a bal oldalon egy 'value' típusba egy 'reference' típusú értéket próbálunk elhelyezni. Ennek során a rendszer "elballag" a referencia típusú változó által jelzett memóriaterületre, és ha az ott tárolt érték típusa megfelel a típuskonverziónak, akkor azt onnan visszamásolja a fogadó változó ('c') memóriaterületére.

Ez a lépés automatikusan (implicit) lezajlik, a 'boxing' művelet ellentéte, a tárolt érték "kicsomagolása", ezért ezt a lépést 'unboxing'-nak nevezzük.

Boxing és Unboxing a gyakorlatban

A fentivel teljesen megegyező lépéssorozat zajlik le például az 'ArrayList' használata során. Az 'ArrayList' osztály '.Add(...)' metódusának paramétere ugyanis 'Object' típusú:

class ArrayList
{
 public void Add( Object o )
 {
   ....
 }
}
// használata
ArrayList l = new ArrayList();
int x = 12;
l.Add( x );

A fenti 'l.Add(x)' metódushívás során a háttérben az 'o=x' hajtódik végre (a paraméterváltozó felveszi a hívás helyén feltüntetett értéket). Ez gyakorlatilag egy 'boxing' műveletet is jelent a fenti példában.

Amikor az 'ArrayList'-ből kiveszünk egy benne korábban elhelyezett értéket, akkor pedig ennek fordítottja zajlik le. Az 'ArrayList' indexelője ugyanis 'Object' típusú értéket ad vissza:

class ArrayList
{
 public Object this[int index]
 {
   get
   {
       ....
   }
 }
}
// használata
ArrayList l = new ArrayList();
int x = 12;
l.Add( x );
...
int y = (int)l[0];

Az 'y = (int)l[0]' során az jobb oldalon található 'Object' típusú értékre egy 'unboxing' lépés is végrehajtódik, hogy a tárolt 'int' típusú értéket az 'y' változóba vissza tudjuk nyerni.

A Generic típusok haszna

A fenti példa is bizonyítja, hogy 'ArrayList'-ben 'int' típusú értéket tárolni igen gazdaságtalan:

  • a lista nem magát az 'int' értéket tárolja, csak annak a memóriacímét
  • berakáskor 'boxing' művelet zajlik a háttérben, az 'int' típusú érték lemásolásra kerül
  • kiolvasáskor egy 'unboxing' művelet zajlik le

Így egy 'int' típusú érték tárolásának memóriaigénye relatíve nagyon magas, egyrészt maga az 'int' érték tárolásán felül (4 byte) igényel egy memóriacím tárkapacitását is (szintén 4 byte).

A fenti nem túl hatékony megoldás helyett ezért ilyen esetben használjuk a 'Generic' lista típust, mely 'value type' típusú értékek tárolására minden szempontból sokkal hatékonyabb megoldást nyújt, és egyéb előnyökkel is rendelkezik!

Hernyák Zoltán
A lap eredeti címe: „http://wiki.ektf.hu/wiki/Mp3/ea25
Nézetek
nincs sb_34.231.21.123 cikk