The fly

Serializeを使ったコードを書いていて、readonlyなフィールドをNonSerializedにしたらどうなるか気になりました。

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;

[Serializable]
class Bar 
{
    [NonSerialized]
    readonly string name = "太郎";
    [NonSerialized]
    readonly int age = 20;
    [NonSerialized]
    readonly List<string> collection = new List<string> ();

    public string Name { get { return name; } }
    public int Age { get { return age; } }
    public List<string> Collection { get { return collection; } }
}

class Program
{
    static void Main (string[] args)
    {
        var bf = new BinaryFormatter ();
        var ms = new MemoryStream ();

        var bar = new Bar ();
        bf.Serialize (ms,bar);

        ms.Position = 0;
        bar = bf.Deserialize (ms) as Bar;

        Console.WriteLine ("Name={0}, Age={1}, Collection={2}",(object)bar.Name ?? "<NULL>",bar.Age,(object)bar.Collection ?? "<NULL>");
    }
}

/*
 結果
 Name=<NULL>, Age=0, Collection=<NULL>
 */

コンストラクタは呼び出されず、readonlyフィールドはデフォルト値のまま。Serializeで転送された太郎君は、もう、太郎君では無かったということに。(^^;

特殊な例ですが、フィールドの一部に導出可能な項目(例えば他のフィールドからDictionaryを作るなど)があり、それをシリアライズ対象から外して、IDeserializationCallbackで構築しようとすると、導出フィールドはreadonlyに出来ないんですよね。ISerializeを実装して、Foo(SerializationInfo info, StreamingContext context)というデシリアライズ用コンストラクタを用意すれば良いのですが、個人的には、この特定の引数を持ったコンストラクタってところが気に入らないです。readonlyフィールドにはコンストラクタでしか初期化出来ないのでコンストラクタが必要なのは分かりますが、何かモヤモヤするものがあります。