ぼやきごと/2011-05-28/自動プロパティのシリアライズ(C# 3.0) のバックアップ差分(No.1)


  • 追加された行はこの色です。
  • 削除された行はこの色です。
#blog2navi()
*自動プロパティのシリアライズ(C# 3.0) [#e88f8464]

C# 3.0 には自動プロパティという言語仕様がある。~
例えば次のように書く。

#code(csharp){{
public class Sample
{
    public int Number { get; set; }

    public string Text { get; set; }

    public double[] Values { get; set; }
}
}}

一見すると @code{abstract}; なプロパティのようにも見えるが、こう書くことでコンパイラが @code{private}; なフィールドを自動で生成してくれる。~
即ち次のように記述した場合と同等になる(ただし自動生成されるフィールドにコード上からアクセスすることはできない)。

#code(csharp){{
public class Sample
{
    private int _number;
    private string _text;
    private double[] _values;

    public int Number
    {
        get { return _number; }
        set { _number = value; }
    }

    public string Text
    {
        get { return _text; }
        set { _text = value; }
    }

    public double[] Values
    {
        get { return _values; }
        set { _values = value; }
    }
}
}}

@code{public}; なフィールドと何の違いがあるんだと思うかもしれないが、後々の保守のことを考えるとフィールドにはプロパティを通してアクセスさせた方がよい。~
また、次に示すような書き方をすることで、クラス内や派生クラス内からのみ値を参照/設定できるプロパティを簡潔な記述で実装できる。

#code(csharp){{
public class Sample
{
    public int Number { get; protected set; }

    public string Text { get; private set; }

    protected double[] Values { get; private set; }
}
}}

…と、ここまでは自動プロパティの説明で、ここからが本題。

上述のようにフィールドを省略できる自動プロパティだが、その場合にその値はきちんとシリアライズされるのだろうか。~
また、されるとすればどのような名前でシリアライズされるのだろうか。

というわけで @code{SoapFormatter}; でシリアライズする簡単な実験コードを書いてみた。~
比較のために、自動プロパティを使わないクラスもシリアライズしてみている。

#code(csharp){{
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Soap;

namespace SerializeSample
{
    /// <summary>
    /// 自動プロパティ使用クラス。
    /// </summary>
    [Serializable]
    class Auto
    {
        public int Number { get; set; }
        public string Text { get; set; }
        public double[] Values { get; set; }
    }

    /// <summary>
    /// 自動プロパティ非使用クラス。
    /// </summary>
    [Serializable]
    class Maunal
    {
        private int _number;
        private string _text;
        private double[] _values;

        public int Number
        {
            get { return _number; }
            set { _number = value; }
        }

        public string Text
        {
            get { return _text; }
            set { _text = value; }
        }

        public double[] Values
        {
            get { return _values; }
            set { _values = value; }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Auto auto = new Auto();
            Maunal manual = new Maunal();

            // プロパティ設定
            auto.Number = manual.Number = 100;
            auto.Text = manual.Text = "あいうえお";
            auto.Values = manual.Values = new double[] { 1.5, -4.0 };

            // シリアライズ
            SoapFormatter formatter = new SoapFormatter();
            using (FileStream fs = File.OpenWrite("auto.xml"))
            {
                formatter.Serialize(fs, auto);
            }
            using (FileStream fs = File.OpenWrite("manual.xml"))
            {
                formatter.Serialize(fs, manual);
            }
        }
    }
}
}}

実行した結果出力されたファイルの内容は次の通り。~
なお、名前空間指定などは長い&違いがないため省略している。

:auto.xml|
#code(pre){{
<SOAP-ENV:Envelope (…中略…)>
<SOAP-ENV:Body>
<a1:Auto id="ref-1" (…中略…)>
<_x003C_Number_x003E_k__BackingField>100</_x003C_Number_x003E_k__BackingField>
<_x003C_Text_x003E_k__BackingField id="ref-3">あいうえお</_x003C_Text_x003E_k__BackingField>
<_x003C_Values_x003E_k__BackingField href="#ref-4"/>
</a1:Auto>
<SOAP-ENC:Array id="ref-4" SOAP-ENC:arrayType="xsd:double[2]">
<item>1.5</item>
<item>-4</item>
</SOAP-ENC:Array>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
}}
:manual.xml|
#code(pre){{
<SOAP-ENV:Envelope (…中略…)>
<SOAP-ENV:Body>
<a1:Maunal id="ref-1" (…中略…)>
<_number>100</_number>
<_text id="ref-3">あいうえお</_text>
<_values href="#ref-4"/>
</a1:Maunal>
<SOAP-ENC:Array id="ref-4" SOAP-ENC:arrayType="xsd:double[2]">
<item>1.5</item>
<item>-4</item>
</SOAP-ENC:Array>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
}}

主に違うのは4〜6行目で、自動プロパティ使用クラスでは何やら長ったらしいフィールド名が使われている。~
どうやらこれがコンパイラの自動生成するフィールドのようだ。

というわけで、まぁ当然ではあるのだが自動プロパティの値もきちんとシリアライズされた。~
なお、値をシリアライズしないことを示す @code{NonSerializedAttribute}; 属性はフィールドにしか指定できないので、シリアライズしたくない値については省略せずにフィールドを記述する必要がある。

最後に、上述の自動プロパティ使用クラスにちょっといじわるなコードを追加してみた。

#code(csharp){{
    /// <summary>
    /// 自動プロパティ使用クラス。
    /// </summary>
    [Serializable]
    class Auto
    {
        private int _x003C_Number_x003E_k__BackingField = 10;

        public int Number { get; set; }
        public string Text { get; set; }
        public double[] Values { get; set; }
    }
}}

コンパイラが自動生成するフィールド名と同じ名前のフィールドを定義してみた。~
このクラスをシリアライズすると、次のように出力された。

#code(pre){{
<SOAP-ENV:Envelope (…中略…)>
<SOAP-ENV:Body>
<a1:Auto id="ref-1" (…中略…)>
<_x005F_x003C_Number_x005F_x003E_k__BackingField>10</_x005F_x003C_Number_x005F_x003E_k__BackingField>
<_x003C_Number_x003E_k__BackingField>100</_x003C_Number_x003E_k__BackingField>
<_x003C_Text_x003E_k__BackingField id="ref-3">あいうえお</_x003C_Text_x003E_k__BackingField>
<_x003C_Values_x003E_k__BackingField href="#ref-4"/>
</a1:Auto>
<SOAP-ENC:Array id="ref-4" SOAP-ENC:arrayType="xsd:double[2]">
<item>1.5</item>
<item>-4</item>
</SOAP-ENC:Array>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
}}

追加されたのは4行目で、ご覧のように変数名の頭に @code{_x005F}; という文字列が追加されている。~
既存のフィールドと名前が被る場合、自動生成するフィールドではなく、既存のフィールドの方の名前を変更しているようだ。

RIGHT:Category: &#x5b;[[C#>ぼやきごと/カテゴリ/C#]]&#x5d;&#x5b;[[プログラミング>ぼやきごと/カテゴリ/プログラミング]]&#x5d; - 2011-05-28 18:04:53
----
RIGHT:&blog2trackback();
#comment(above)
#blog2navi()