ISerializable で自作シリアライズを実装する際、デシリアライズ処理におけるコレクション(配列やList)の扱いには注意が必要である。
コレクションのデシリアライズ直後、中身はまだデシリアライズされてない。
[ISerializable サンプル]
※本来、これらのプロパティだけなら ISerializable の実装は不要
上記オブジェクトをシリアライズ→デシリアライズしてみる。
結果は、
コレクションのデシリアライズ直後は、中身が null であることが分かる。
その後、コレクションの中身のデシリアライズが実行されている。
つまり、コレクションのデシリアライズは、コレクション本体→中身の順で行われる。
例えばシリアライズ対象外のプロパティ(Parentとか)を設定したり、独自のAddメソッドなどで追加したい場合など。
そのために、OnDeserializedAttribute が存在する。
これを付けたメソッドは、コレクションの中身がデシリアライズされた後に呼び出される。
[OnDeserialized を追加した ISerializable サンプル]
上と同じ方法でシリアライズ→デシリアライズした結果は、
コレクションの中身がデシリアライズされているので、変更が可能。
コレクションのデシリアライズ直後、中身はまだデシリアライズされてない。
[ISerializable サンプル]
※本来、これらのプロパティだけなら ISerializable の実装は不要
[Serializable]
public class Node<T> : ISerializable
{
public Node(){}
public T Value;
public readonly List<Node<T>> Children = new List<Node<T>>();
private Node(SerializationInfo info, StreamingContext context)
{
Value = (T)info.GetValue("Value", typeof(T));
Children = (List<Node<T>>)info.GetValue("Children", typeof(List<Node<T>>));
Console.WriteLine("コレクションの個数 : {0}", Children.Count);
foreach( var child in Children ) Console.WriteLine(child == null);
}
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter=true)]
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Value", Value);
info.AddValue("Children", Children);
}
}
上記オブジェクトをシリアライズ→デシリアライズしてみる。
static class TestSerialization
{
static void Main()
{
var node = new Node<string>() { Value = "parent" };
node.Children.Add(new Node<string>() { Value = "one" });
node.Children.Add(new Node<string>() { Value = "two" });
var mem = new MemoryStream();
var formatter = new BinaryFormatter();
formatter.Serialize(mem, node);
mem.Position = 0;
var deserialized = (Node<string>)formatter.Deserialize(mem);
Console.WriteLine("デシリアライズ後");
Console.WriteLine(deserialized.Children[0].Value);
Console.WriteLine(deserialized.Children[1].Value);
}
}
結果は、
コレクションの個数 : 2 True True コレクションの個数 : 0 コレクションの個数 : 0 デシリアライズ後 one two
コレクションのデシリアライズ直後は、中身が null であることが分かる。
その後、コレクションの中身のデシリアライズが実行されている。
つまり、コレクションのデシリアライズは、コレクション本体→中身の順で行われる。
OnDeserializedAttribute
デシリアライズ処理において、コレクションの中身にいじりたい場合はどうするのか?例えばシリアライズ対象外のプロパティ(Parentとか)を設定したり、独自のAddメソッドなどで追加したい場合など。
そのために、OnDeserializedAttribute が存在する。
これを付けたメソッドは、コレクションの中身がデシリアライズされた後に呼び出される。
[OnDeserialized を追加した ISerializable サンプル]
[Serializable]
public class Node<T> : ISerializable
{
public Node(){}
public T Value;
public readonly List<Node<T>> Children = new List<Node<T>>();
private Node(SerializationInfo info, StreamingContext context)
{
Value = (T)info.GetValue("Value", typeof(T));
Children = (List<Node<T>>)info.GetValue("Children", typeof(List<Node<T>>));
Console.WriteLine("コレクションの個数 : {0}", Children.Count);
foreach( var child in Children ) Console.WriteLine(child == null);
}
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
Console.WriteLine("OnDeserialized : Value={0}", Value);
foreach( var child in Children ) Console.WriteLine(child.Value);
}
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter=true)]
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Value", Value);
info.AddValue("Children", Children);
}
}
上と同じ方法でシリアライズ→デシリアライズした結果は、
コレクションの個数 : 2 True True コレクションの個数 : 0 コレクションの個数 : 0 OnDeserialized : Value=parent one two OnDeserialized : Value=one OnDeserialized : Value=two デシリアライズ後 one two
コレクションの中身がデシリアライズされているので、変更が可能。
0 件のコメント:
コメントを投稿