All english written articles have English article tag and lays under horizontal line. First part is written in Czech.

sobota 21. července 2012

XNA a Serializace III. / XNA and serialization III.

Včera jsem napsal o třídách, které se postarají o ukládání dat do souborů samotných a dnes se podíváme na to, jak je využít.
Každá třída, která má být serializována na to musí být uzpůsobena. Možností je několik. Já jsem nakonec vybral možnost tuto. Každá třída má virtuální metodu Serialize, ta se stará o ukládání a speciální konstruktor, ten se stará o načítání. RunUO to má kapku jinak, ale tato možnost se mi zdála být lepší. Kupříkladu takto:
using System;

namespace VodacekEngine {
    public class Priklad {
        public Priklad(){
        }

        public Priklad(GenericReader reader){
            int version=reader.ReadInt();
            //zde nacitani promennych
        }
 
        public virtual void Serialize(GenericWriter writer){
            writer.Write(0);//verze, k cemu se pouziva vice dale
        }
    }
}

Verze je pro všechny objekty stejná, vřele doporučuji ji tam mít. Slouží k tomu, když bude potřeba nějakou proměnnou přidat, nebo odebrat. Pokud by tam nebyla nastal by při načítání problém a obsah se načetl špatně, nebo by skončil chybou. Nejčastěji pokus o čtení za koncem souboru.
Pokud chceme přidat novou proměnnou jednoduše zvedneme proměnnou s verzí a do čtení pak na verzi dáme podmínku kupříkladu přidáme naší třídě proměnnou jméno:
using System;

namespace VodacekEngine {
    public class Priklad {
        public string Jmeno;
        public Priklad(){
        }

        public Priklad(GenericReader reader){
            int version=reader.ReadInt();
            if(version>=1)Jmeno=reader.ReadString();
            else Jmeno=null;//nebo jina pocatecni hodnota
        }
 
        public virtual void Serialize(GenericWriter writer){
            writer.Write(1);//<------ zvednuta o 1
            writer.Write(Jmeno);
        }
    }
}

Místo podmínky, lze užít třeba switch a case, důležité je zvednout verzi a číst a zapisovat ve stejném pořadí.
Pokud by bylo potřeba proměnnou Jmeno nopak zrušit, tak opět zvedneme verzi o jedničku. Zapisování proměnné smažeme a upravíme načítání:
using System;

namespace VodacekEngine {
    public class Priklad {

        public Priklad(){
        }

        public Priklad(GenericReader reader){
            int version=reader.ReadInt();
            if(version==1)reader.ReadString();//promennou nacteme, ale neukladame
        }
 
        public virtual void Serialize(GenericWriter writer){
            writer.Write(2);//<------ zvednuta o 1
        }
    }
}
Proměnnou načteme, ale nikam ji neuložíme. (ono taky mezi námi není kam ukládat když ta proměnná neexistuje) Načítání je potřeba provést tak jako tak, jinak hodnoty uložené za touto by byly načteny špatně. Podmínka se také mírně změnila. Tentokráte testujeme pouze na tu jednu verzi ve které se proměnná vyskytovala.
Dalším problematickým případem je ukládání polí. Používá se tam jeden fígl. Pojďme se podívat na naši ukázkovou třídu:
using System;

namespace VodacekEngine {
    public class Priklad {
        public string[] Jmena;
        public Priklad(){
            Jmena=new string[]{"Pepa","Jernom","Kucir","Jelen"};
        }

        public Priklad(GenericReader reader){
            int version=reader.ReadInt();
            if(version>=1){
                int pocet=reader.ReadInt();//nacteme pocet prvku
                Jmena=new string[pocet];//vytvorime pole o poctu prvku
                for(int i=0;i>pocet;i++){
                    Jmena[i]=reader.ReadString();//postupne nacteme prvky pole
                }
            }
            else Jmena=new string[]{"Pepa","Jernom","Kucir","Jelen"};//initializace
        }
 
        public virtual void Serialize(GenericWriter writer){
            writer.Write(1);//<------ zvednuta o 1
            writer.Write(Jmena.Length);//zapiseme pocet prvku
            for(int i=0;i<Jmena.Length;i++){
                writer.Write(Jmena[i]);//zapisuje vsechny prvky
            }
        }
    }
}
Nejprve se zapíše počet prvků pole a poté prvky samotné. Při načítání se načte počet a for cyklem se provede načítání jednotlivých prvků.

Tak to je pro dnešek vše. Příště (snad zítra) se podíváme na kod, který řídí zápis a čtení do souborů a který výše uvedené metody volá.



In my last English article I wrote something about classes that are used to saving and loading types from files. In this article I will write something about implementation of reading and writing code in each class, that is need to be serialized.
Each class have one virtual method Serialize (this method is used to write data to file) and special constructor for reading values from file. RunUO (ultima online emulator) is using slight different idea, but I thing that this is better for our purpose. Let's have this simple example class:
using System;

namespace VodacekEngine {
    public class Example {
        public Example(){
        }

        public Example(GenericReader reader){
            int version=reader.ReadInt();
            //here reading values
        }
 
        public virtual void Serialize(GenericWriter writer){
            writer.Write(0);//version, I will discus it later
        }
    }
}
Variable called version is very important. This variable is used when you want to add or remove some variable from serialization. If this variable don't exist, you can get into some serious trouble.
If we want to add new variable, we must increase version. Here is example with our class:
using System;

namespace VodacekEngine {
    public class Example {
        public string Name;
        public Exammple(){
        }

        public Example(GenericReader reader){
            int version=reader.ReadInt();
            if(version>=1)Name=reader.ReadString();
            else Name=null;//here belong initial value
        }
 
        public virtual void Serialize(GenericWriter writer){
            writer.Write(1);//<------ increased
            writer.Write(Name);
        }
    }
}
If is version grater than one variable is read from file, if not we should initialize it.Writing is more simple. Only version is increased and added new variable.

If we need to remove variable Name simply again increase version and remove writing part. In reading part simply add if statement for last version where variable exist. Something like this:
using System;

namespace VodacekEngine {
    public class Example {

        public Example(){
        }

        public Example(GenericReader reader){
            int version=reader.ReadInt();
            if(version==1)reader.ReadString();//variable MUST be read, but is not stored
        }
 
        public virtual void Serialize(GenericWriter writer){
            writer.Write(2);//<------ increased
        }
    }
}
Removed variable NEED to be read but is not stored. Variable is not exist any more.
Our last example is writing arrays. There is used little trick. Here is our example class:
using System;

namespace VodacekEngine {
    public class Example {
        public string[] Names;
        public Example(){
            Names=new string[]{"Pepa","Jernom","Kucir","Jelen"};
        }

        public Example(GenericReader reader){
            int version=reader.ReadInt();
            if(version>=1){
                int count=reader.ReadInt();//count of items in array
                Names=new string[count];//creates array
                for(int i=0;i>pocet;i++){
                    Names[i]=reader.ReadString();//reading members of array
                }
            }
            else Names=new string[]{"Pepa","Jernom","Kucir","Jelen"};//initialize
        }
 
        public virtual void Serialize(GenericWriter writer){
            writer.Write(1);//<------ increased
            writer.Write(Names.Length);//write count of members in array
            for(int i=0;i<Names.Length;i++){
                writer.Write(Names[i]);//write each member
            }
        }
    }
}
First of all is written count of members in array and then in for loop all members. Version is off course increased. In reading part, is standard if statement for adding values. Then is read count of members in array. Array is created and then in for loop added variables. Really simple.
That is all for today. In next article I will show you code that is used for calling this methods.

Žádné komentáře:

Okomentovat