BinaryFormatter 経由で、オブジェクトをシリアライズとデシリアライズ
シリアライズ対象。
[Serializable] public class Game : IDeserializationCallback { public HardwereType[] Hard { get; set; } public String Title { get; set; } public String Genre { get; set; } public uint Price { get; set; } public override string ToString() { return string.Format( "Title:{0}, Genre:{1}, Hardwere:{3} Price:{2}", Title, Genre, Price, HardList(Hard)); } private static string HardList(HardwereType[] list) { StringBuilder builder = new StringBuilder(); foreach (HardwereType data in list) { builder.Append(data); builder.Append(' '); } return builder.ToString(); } } [Serializable] public enum HardwereType { PS3, XBox360, PSP, Wii, DS }
Game vanquish = new Game() { Genre = "FPS", Hard = new HardwereType[2] { HardwereType.PS3, HardwereType.XBox360}, Price = 7980, Title = "VANQUISH" }; BinaryFormatter formatter = new BinaryFormatter(); using (FileStream writer = new FileStream("sample.dat", FileMode.Create)) { formatter.Serialize(writer, vanquish); } using (FileStream reader = new FileStream("sample.dat", FileMode.Open)) { Game loaded = (Game)formatter.Deserialize(reader); Console.WriteLine(loaded); }
ここで、互換性を保ったままクラスを拡張してみる。
(Ageの実装はObject指向的に間違ってるけど、サンプルの為)
[Serializable] public class Game : IDeserializationCallback { /* 略 */ [OptionalField] public String CERO = "A"; [NonSerialized] public int Age; public override string ToString() { return string.Format( "Title:{0}, CERO:{4}({5}), Genre:{1}, Hardwere:{3} Price:{2}", Title, Genre, Price, HardList(Hard), CERO, Age); } private static string HardList(HardwereType[] list) { StringBuilder builder = new StringBuilder(); foreach (HardwereType data in list) { builder.Append(data); builder.Append(' '); } return builder.ToString(); } void IDeserializationCallback.OnDeserialization(object sender) { switch (this.CERO) { case "A": this.Age = 0; break; case "B": this.Age = 12; break; case "C": this.Age = 15; break; case "D": this.Age = 17; break; case "Z": this.Age = 18; break; default: this.Age = 0; break; } } }
これで、プログラムを下記だけにして走らせてみる。
BinaryFormatter formatter = new BinaryFormatter(); using (FileStream reader = new FileStream("sample.dat", FileMode.Open)) { Game loaded = (Game)formatter.Deserialize(reader); Console.WriteLine(loaded); }
重要なのは以下の点か。
- Serialized なフィールドは削除しない。
- NonSerialized 属性は、既存項目に設定しない。
- フィールドの型変更や、名称変更をしない。
- optional field atttibute を新規フィールドに突っ込み
- カスタムなserialization を定義してバージョン差を埋める
- NonSerialized 属性を既存項目から削除する際は、OptionalField 属性を付ける。
- OptionalFirld には、意味のあるデフォルト値を設定する。