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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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í:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 } } } |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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 } } } } |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 } } } |
If we want to add new variable, we must increase version. Here is example with our class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 } } } |
Our last example is writing arrays. There is used little trick. Here is our example class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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 } } } } |
That is all for today. In next article I will show you code that is used for calling this methods.
Žádné komentáře:
Okomentovat