バージョン トレラントなシリアル化

.NET Framework のバージョン 1.0 および 1.1 では、アプリケーションのあるバージョンから次のバージョンに移行しても再利用できる、シリアル化可能な型の作成に問題がありました。 フィールドを追加して型を変更すると、次のような問題が発生していました。

  • 以前のバージョンのアプリケーションは、古い型の新しいバージョンを逆シリアル化するように要求すると例外をスローする。

  • 新しいバージョンのアプリケーションは、データが欠落している以前のバージョンの型を逆シリアル化すると例外をスローする。

バージョン トレラントなシリアル化 (VTS: Version Tolerant Serialization) は、.NET Framework 2.0 で導入された機能セットで、シリアル化可能な型を、長期にわたって簡単に変更できるようにします。 具体的には、VTS 機能が、ジェネリック型を含め、SerializableAttribute 属性が適用されているクラスに対して有効です。 VTS を使用すると、他のバージョンの型との互換性を失うことなく、これらのクラスに新しいフィールドを追加できます。 実際に動作するサンプル アプリケーションについては、「バージョン間の耐性があるシリアル化に対応する技術サンプル」を参照してください。

VTS 機能は、BinaryFormatter を使用する場合に有効になります。 また、SoapFormatter を使用する場合は、外部データの複数バージョン対応機能を除くすべての機能が有効になります。 シリアル化でこれらのクラスを使用する方法の詳細については、「バイナリ シリアル化」を参照してください。

機能の一覧

この機能セットの内容は次のとおりです。

  • 外部データまたは予期しないデータの複数バージョン対応。 これにより、型の新しいバージョンから以前のバージョンにデータを送信できます。

  • 欠落しているオプション データの複数バージョン対応。 これにより、以前のバージョンから新しいバージョンにデータを送信できます。

  • シリアル化のコールバック。 これにより、データが欠落している場合に、既定値の高度な設定ができます。

さらに、オプション フィールドが新たに追加されたときに宣言を生成する機能があります。 これは、OptionalFieldAttribute 属性の VersionAdded プロパティです。

これらの機能の詳細については、以下で説明します。

外部データまたは予期しないデータの複数バージョン対応

これまで、逆シリアル化中に外部データまたは予期しないデータが検出されると、例外がスローされていました。 VTS ではこのような場合、例外がスローされるのではなく、外部データまたは予期しないデータがすべて無視されます。 これにより、新しいバージョンの型 (フィールドが追加されたバージョン) を使用するアプリケーションが、同じ型の以前のバージョンを必要とするアプリケーションに情報を送ることができます。

次の例では、バージョン 2.0 の Address クラスの CountryField に含まれる追加データは、以前のアプリケーションで新しいバージョンを逆シリアル化する場合、無視されます。

// Version 1 of the Address class.
[Serializable]
public class Address
{
    public string Street;
    public string City;
}
// Version 2.0 of the Address class.
[Serializable]
public class Address
{
    public string Street;
    public string City;
    // The older application ignores this data.
    public string CountryField;
}
' Version 1 of the Address class.
<Serializable> _
Public Class Address
    Public Street As String
    Public City As String
End Class

' Version 2.0 of the Address class.
<Serializable> _
Public Class Address
    Public Street As String
    Public City As String
    ' The older application ignores this data.
    Public CountryField As String
End Class

欠落しているデータの複数バージョン対応

OptionalFieldAttribute 属性をフィールドに適用すると、そのフィールドをオプションとしてマークできます。 逆シリアル化中に、オプション データの欠落が検出されても、シリアル化エンジンはデータが存在しないことを無視し、例外をスローしません。 そのため、以前のバージョンの型を必要とするアプリケーションから、同じ型の新しいバージョンを必要とするアプリケーションにデータを送信できます。

バージョン 2.0 の Address クラスで、CountryField フィールドをオプションとしてマークする方法を次の例に示します。 バージョン 2.0 を必要とする新しいアプリケーションに対して、以前のアプリケーションがバージョン 1 を送信した場合、データが存在しないことは無視されます。

[Serializable]
public class Address
{
    public string Street;
    public string City;

    [OptionalField]
    public string CountryField;
}
<Serializable> _
Public Class Address
    Public Street As String
    Public City As String

    <OptionalField> _
    Public CountryField As String
End Class

シリアル化のコールバック

シリアル化のコールバックは、次の 4 つの時点でシリアル化プロセスおよび逆シリアル化プロセスにフックする機構です。

属性

関連するメソッドが呼び出されるタイミング

一般的な用途

OnDeserializingAttribute

逆シリアル化の前 *

オプション フィールドの既定値を初期化します。

OnDeserializedAttribute

逆シリアル化の後

他のフィールドの内容に基づいて、オプション フィールドの値を修正します。

OnSerializingAttribute

シリアル化の前

シリアル化の準備をします。 たとえば、オプションのデータ構造を作成します。

OnSerializedAttribute

シリアル化の後

シリアル化イベントのログを記録します。

* このコールバックは、逆シリアル化コンストラクターの前に呼び出されます (存在する場合)。

ms229752.collapse_all(ja-jp,VS.120).gifコールバックの使用

コールバックを使用するには、StreamingContext パラメーターを受け取るメソッドに、適切な属性を適用します。 各属性でマークできるのは、クラスごとに 1 つのメソッドだけです。 次に例を示します。

[OnDeserializing]
private void SetCountryRegionDefault(StreamingContext sc)
{
    CountryField = "Japan";
}
<OnDeserializing>
Private Sub SetCountryRegionDefault(StreamingContext sc)
    CountryField = "Japan";
End Sub

これらのメソッドの使用目的は、バージョン管理です。 オプション フィールドにデータがない場合、逆シリアル化の実行中に、このフィールドが正しく初期化されないことがあります。 この問題を解決するには、正しい値を割り当てるメソッドを作成してから、このメソッドに OnDeserializingAttribute 属性または OnDeserializedAttribute 属性のいずれかを適用します。

型のコンテキストで使用されるメソッドを次に示します。 以前のバージョンのアプリケーションが新しいバージョンのアプリケーションに Address クラスのインスタンスを送信すると、CountryField フィールドのデータが欠落します。 しかし、逆シリアル化を行うと、このフィールドは既定値である "Japan" に設定されます。

[Serializable]
public class Address
{
    public string Street;
    public string City;
    [OptionalField]
    public string CountryField;

    [OnDeserializing]
    private void SetCountryRegionDefault (StreamingContext sc)
    {
        CountryField = "Japan";
    }
}
<Serializable> _
Public Class Address
    Public Street As String
    Public City As String
    <OptionalField> _
    Public CountryField As String

    <OnDeserializing> _
    Private Sub SetCountryRegionDefault(StreamingContext sc)
        CountryField = "Japan";
    End Sub
End Class

VersionAdded プロパティ

OptionalFieldAttribute には、VersionAdded プロパティがあります。 .NET Framework バージョン 2.0 では、このプロパティは使用されません。 ただし、このプロパティを正しく設定して、型が今後のシリアル化エンジンと互換性を保つようにすることが重要です。

このプロパティは、指定されたフィールドに追加された型のバージョンを示します。 次の例に示すように、このプロパティの値は 2 を開始値として、型が変更されるたびに常に 1 ずつインクリメントする必要があります。

// Version 1.0
[Serializable]
public class Person
{
    public string FullName;
}

// Version 2.0
[Serializable]
public class Person
{
    public string FullName;

    [OptionalField(VersionAdded = 2)]
    public string NickName;
    [OptionalField(VersionAdded = 2)]
    public DateTime BirthDate;
}

// Version 3.0
[Serializable]
public class Person
{
    public string FullName;

    [OptionalField(VersionAdded=2)]
    public string NickName;
    [OptionalField(VersionAdded=2)]
    public DateTime BirthDate;

    [OptionalField(VersionAdded=3)]
    public int Weight;
}
' Version 1.0
<Serializable> _
Public Class Person
    Public FullName
End Class

' Version 2.0
<Serializable> _
Public Class Person
    Public FullName As String

    <OptionalField(VersionAdded := 2)> _
    Public NickName As String
    <OptionalField(VersionAdded := 2)> _
    Public BirthDate As DateTime
End Class

' Version 3.0
<Serializable> _
Public Class Person
    Public FullName As String

    <OptionalField(VersionAdded := 2)> _
    Public NickName As String
    <OptionalField(VersionAdded := 2)> _
    Public BirthDate As DateTime

    <OptionalField(VersionAdded := 3)> _
    Public Weight As Integer
End Class

SerializationBinder

サーバー上とクライアント上では異なるバージョンのクラスが必要なため、ユーザーによっては、シリアル化するクラスと逆シリアル化するクラスを制御することが必要になる場合があります。 SerializationBinder は、シリアル化中および逆シリアル化中に使用される実際の型を制御するために使用される抽象クラスです。 このクラスを使用するには、クラスを SerializationBinder から派生させ、BindToName メソッドと BindToType メソッドをオーバーライドします。 詳細については、次のトピックを参照してください。 SerializationBinder を使用したシリアル化および逆シリアル化の制御.

ベスト プラクティス

バージョン管理が正しく行われるように、バージョン間で型を変更するときは次の規則に従ってください。

  • シリアル化したフィールドを削除しない。

  • 以前のバージョンでフィールドに NonSerializedAttribute 属性が適用されていない場合、新しいバージョンでもこの属性を適用しない。

  • シリアル化したフィールドの名前または型を変更しない。

  • 新しいシリアル化フィールドを追加する場合は、OptionalFieldAttribute 属性を適用する。

  • 以前のバージョンでシリアル化できなかったフィールドから NonSerializedAttribute 属性を削除する場合は、OptionalFieldAttribute 属性を適用する。

  • すべてのオプション フィールドに対して、既定値として 0 または null が許容される場合以外は、シリアル化コールバックを使用して意味のある既定値を設定する。

型が今後のシリアル化エンジンと互換性を保つようにするには、次のガイドラインに従ってください。

  • OptionalFieldAttribute 属性の VersionAdded プロパティを常に正しく設定する。

  • バージョンの分岐は避ける。

参照

関連項目

SerializableAttribute

BinaryFormatter

SoapFormatter

VersionAdded

OptionalFieldAttribute

OnDeserializingAttribute

OnDeserializedAttribute

OnDeserializingAttribute

OnSerializedAttribute

StreamingContext

NonSerializedAttribute

その他の技術情報

バイナリ シリアル化