デシリアライズではまった
次のサンプルコードのMyMapクラスのようなクラスをファイルにシリアライズしてデシリアライズすると、デシリアライズ用のコンストラクタの中で_entriesの要素が全部(null,null)になってしまう。entry.Item1とentry.Item2を使って処理をしていたので、実行時エラーになっていた。そしてその理由が分からず数週間悩んだ。
Testメソッドで、デシリアライズ済みのオブジェクトを操作するときは、_entriesには正しい値が入っているので、ちゃんとTryGetValueを実行できている。entry.Item1やentry.Item2に依存した何らかの初期化処理をしたい場合、どうしたら良いのかな?
using System; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; namespace ConsoleApplication1 { [Serializable] class MyString : ISerializable { string _value; public MyString(string str) { _value = str; } protected MyString(SerializationInfo info, StreamingContext context) { _value = info.GetString("Value"); } public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("Value", _value); } public string Value { get { return _value; } set { _value = value; } } public override string ToString() { return Value; } } [Serializable] class MyMap : ISerializable { int _n; IList<Tuple<MyString, MyString>> _entries; public MyMap() { _n = 0; _entries = new List<Tuple<MyString, MyString>>(); } protected MyMap(SerializationInfo info, StreamingContext context) { _n = info.GetInt32("N"); Console.WriteLine(_n); _entries = (IList<Tuple<MyString, MyString>>)info.GetValue("Entries", typeof(IList<Tuple<MyString, MyString>>)); foreach (var entry in _entries) { // なぜかすべてのTupleが(null, null)になっている。 Console.WriteLine("{0} -> {1}", entry.Item1, entry.Item2); } } public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("N", _n); info.AddValue("Entries", _entries); } public void Add(MyString val1, MyString val2) { Tuple<MyString, MyString> entry = Tuple.Create(val1, val2); _entries.Add(entry); } public bool TryGetValue(MyString key, out MyString result) { foreach (Tuple<MyString, MyString> entry in _entries) { if (entry.Item1.Value == key.Value) { result = entry.Item2; return true; } } result = null; return false; } public int N { get { return _n; } set { _n = value; } } } class Program { static MyString Key = new MyString("bar"); // 【要修正】ファイル出力先 const string FilePath = @"C:\Users\foobar\Desktop\a.out"; static void Main(string[] args) { MyMap map = new MyMap(); map.Add(new MyString("foo"), new MyString("hoge")); map.Add(new MyString("bar"), new MyString("fuga")); map.Add(new MyString("baz"), new MyString("piyo")); map.N = 42; // シリアライズ → デシリアライズ Test(map); } static void Test(MyMap obj) { MyString result1 = null; obj.TryGetValue(Key, out result1); System.Console.WriteLine(result1); Stream s = Serialize(obj); MyMap tmp = Deserialize(); MyString result2 = null; tmp.TryGetValue(Key, out result2); System.Console.WriteLine(result2); } static Stream Serialize(MyMap obj) { Stream s = null; try { s = new FileStream(FilePath, FileMode.Create); BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(s, obj); } catch (Exception) { throw; } finally { if (s != null) { s.Close(); } } return s; } static MyMap Deserialize() { MyMap ret = null; Stream s = null; try { s = new FileStream(FilePath, FileMode.Open); s.Position = 0; BinaryFormatter bf = new BinaryFormatter(); ret = (MyMap)bf.Deserialize(s); } catch (Exception) { throw; } finally { if (s != null) { s.Close(); } } return ret; } } }