arckép

Neuwirth István

programtervező informatikus MSc – ELTE

Computational and Software Techniques MSc – Cranfield, UK

Elérhetőség
pitta2@gmail.com
+36 30 329 3039

Valid XHTML 1.0 Transitional

Valid CSS!

Szerializáció .NET alatt

A szerializálás során objektumok állapotát rögzítjük valamilyen adatfolyamba, ahonnan később visszaállítható az objektum. Ez a folyamat .Net alatt automatizált, de lehetőségünk van teljes körűen befolyásolni.

Gyakran felmerülő probléma, hogy az alkalmazás egy állapotát elmentsük és később attól a ponttól kezdve folytathassuk a futását. Korábban a fejlesztőknek erre saját megoldásokat kellett készíteni, amely egyrészt növelte a fejlesztésre és tesztelésre szánt időt, másrészt pedig a megvalósítás az adott alkalmazástól függött. A szerializáció egy kész megoldást nyújt a fenti problémára, de segítségét nyújt objektumok távoli elérésében is. Ugyanis napjaink alkalmazásai jórészt nem különálló entitások, hanem hálózaton egymással kapcsolatban álló programok összessége, ahol is meg kell oldani az objektumok távoli elérését a hálózaton keresztül. Több különböző programozási nyelv és keretrendszer is támogatja ma már a szerializáció folyamatát, így például a .Net mellett Java-ban is kihasználhatjuk ezt a lehetőséget.

A szerializációt (sorosítást) és deszerializációt (visszaállítást) valamely, az IFormatter interfészt implementáló osztály végzi. Ennek két függvénye van: a Serialize, amely a kívánt objektum memóriaképét egy adott folyamba (Stream absztrakt osztályból származtatott konkrét folyamba, például FileStream-be) sorosítja, illetve a Deserialize, amely egy adott folyamból visszaállítja az objektumot, és egy erre mutató referenciát ad vissza. Egy ilyen formázóosztály feladata az, hogy a kijelölt objektum, illetőleg az adott objektumból kiindulva felépíthető objektumgráf (ugyanis a mezők lehetnek objektumreferenciák is, így egy rekurzívan bejárható struktúrát kapunk) elemeinek mezőit egy adatfolyamba írja, illetve onnan visszaállítsa az eredeti állapotát. Az, hogy ténylegesen az adatok milyen formában tárolódnak, a formázótól függ. A két leggyakrabban használt formázóosztály a BinaryFormatter (System.Runtime.Serialization.Formatters.Binary névtér), illetve a SoapFormatter (System.Runtime.Serialization.Formatters.Soap névtér), amely XML formátumot használ. Természetesen lehetőségünk van akár saját formázóosztály készítésére is, ha valamilyen konkrét, a fentiektől különböző formátumban szeretnénk az objektumainkat tárolni (fontos lehet korábbi programokkal való kompatibilitáshoz).

Egy konkrét osztályt nagyon egyszerűen tehetünk szerializálhatóvá: az osztálynak adunk egy Serializable attribútumot. Valójában az attribútumok is osztályok, segítségükkel könnyedén egészíthetjük ki metainformációval a kívánt osztályt, tagmezőt, függvényt, stb. A Serializable attribútummal ellátott osztály példányai már sorosíthatóak lesznek.

Szükség lehet arra (és lehetőség is van rá), hogy bizonyos mezőket kihagyjunk a sorosításból. Ez akkor lehet fontos, ha bizonyos mezők nem az objektumállapottal szorosan összefüggő információt hordoznak, hanem valamilyen futásonként különböző értékeket tárolnak. Ilyen például egy adatbáziskapcsolat, vagy a Win32-es rendszer által használt leírók (handler-ek). Az adatbáziskapcsolatot a legközelebbi futás során újra fel kell építeni, a handler-ek sem lesznek már elérhetők, így nincs értelme tárolni őket (sőt, hibás futást eredményezhet, ha már nem létező dolgokra hivatkozunk). Az ilyen mezőket NonSerialized attribútummal kell ellátnunk. Az objektum visszaállítása során azonban ezeket a mezőket is helyre kell állítanunk. Ehhez az osztályunknak implementálnia kell az IDeserializationCallback interfészt, melynek egyetlen OnDeserialization metódusába kell helyezni a nemszerializált mezőket inicializáló kódot.

A sorosítás során tehát egy bonyolult objektumgráfból képezhetünk szekvenciális adatszerkezetet, amely szükséges a fájlba íráshoz, vagy ha az objektumot el szeretnénk küldeni a hálózaton keresztül. A sorosítást, illetve a visszaállítást a .Net reflexió segítségével végzi, így ugyanis lehetőségünk van egy típus adattagjait, függvényeit, azok tulajdonságait, stb. felderíteni, a mezők (legyenek azok akár privátok!) értékeit kiolvasni vagy épp felülírni, az egyes metódusokat meghívni. A privát mezők olvasása/írása ellentmond az egységbezárás elvének, azonban a megfelelő esetben alkalmazva (jelen esetben a szerializáció során) egy rendkívül hatékony eszközt kapunk.

Amennyiben a későbbiekben egy vagy több objektum állapotát szeretnénk elmenteni vagy később használni, a fentiek szerint járjunk el, időt és energiát megspórolva saját megoldások elkészítése helyett, ehhez pedig segítséget nyújthat az alább található példakód.

[Serializable]
class Osztaly : IDeserializationCallback
{
    private int mezo1;
    private int mezo2;

    [NonSerialized]
    private Random rnd;

    public void OnDeserialization(object sender)
    {
        rnd = new Random();
    }

    ...

}

...

MemoryStream mS = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();

// szerializacio 
Osztaly obj1 = new Osztaly();
formatter.Serialize(mS, obj1);

// deszerializacio 
mS.Position = 0;
Osztaly obj2 = (Osztaly)formatter.Deserialize(mS);