版本容错序列化

在 .NET Framework 1.0 和 1.1 版本中,创建可从应用程序的一个版本重用至下一个版本的可序列化类型时会产生问题。 如果通过添加额外字段来修改类型,则会出现以下问题:

  • 要求应用程序的较旧版本反序列化旧类型的新版本时,会引发异常。

  • 应用程序的较新版本反序列化缺少数据的类型的较旧版本时,会引发异常。

版本容错序列化 (VTS) 是 .NET Framework 2.0 中引入的一组功能,它使得修改可序列化类型随着时间推移而变得更加容易。 VTS 功能尤其是为应用了 SerializableAttribute 属性的类而启用的。 VTS 允许向这些类添加新字段,而不破坏与该类型其他版本的兼容性。 有关可运行的示例应用程序,请参见Version Tolerant Serialization Technology Sample

当使用 BinaryFormatter 时,将启用 VTS 功能。 此外,当使用 SoapFormatter 时,也会启用除外来数据容错以外的其他所有功能。 有关将这些类用于序列化的更多信息,请参见二进制序列化

功能列表

功能集包括:

  • 外来或意外数据容错功能。 该功能允许类型的较新版本将数据发送至较旧版本。

  • 缺少可选数据容错功能。 该功能允许较旧版本将数据发送至较新版本。

  • 序列化回调。 该功能在缺少数据的情况下启用智能默认值设置。

此外,添加了新的可选字段时还有声明功能。 这是 OptionalFieldAttribute 属性 (Attribute) 的 VersionAdded 属性 (Property)。

以上功能在下面的内容中详述。

外来或意外数据容错功能

过去在反序列化时,任何外来或意外数据都会导致引发异常。 有了 VTS 后,在相同情况下会忽略所有外来或意外数据,而不会导致引发异常。 这就使得使用类型的较新版本(即包含更多字段的版本)的应用程序可以将信息发送至需要使用同一类型的较旧版本的应用程序。

在下面的示例中,当较旧版本的应用程序反序列化较新版本时,将忽略 Address 类 2.0 版的 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 属性应用于字段,可以将字段标记为可选。 在反序列化期间,如果缺少可选的数据,序列化引擎会忽略这一缺失且不会引发异常。 因此,需要使用类型的较旧版本的应用程序可以将数据发送至需要使用同一类型的较新版本的应用程序。

下面的示例显示的是 Address 类的 2.0 版,其 CountryField 字段标记为可选。 如果较旧版本的应用程序将版本 1 发送至需要使用 2.0 版的较新版本的应用程序,则会忽略数据缺失。

[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

序列化回调

序列化回调是一种机制,它在序列化/反序列化过程中的四个点提供挂钩。

属性 调用关联的方法时 典型用法

OnDeserializingAttribute

反序列化之前。*

初始化可选字段的默认值。

OnDeserializedAttribute

反序列化之后。

根据其他字段的内容修改可选字段值。

OnSerializingAttribute

序列化之前。

准备序列化。 例如,创建可选数据结构。

OnSerializedAttribute

序列化之后。

记录序列化事件。

* 如果有反序列化构造函数,则在该构造函数之前调用此回调。

使用回调

要使用回调,请将相应的属性应用于接受 StreamingContext 参数的方法。 每个类只有一个方法可以用其中每个属性进行标记。 例如:

[OnDeserializing]
private void SetCountryRegionDefault(StreamingContext sc)
{
    CountryField = "Japan";
}
[OnDeserializing]
private void SetCountryRegionDefault(StreamingContext sc)
{
    CountryField = "Japan";
}

这些方法旨在用于版本管理。 在反序列化期间,如果可选字段缺少数据,则可能无法正确初始化该字段。 若要更正这一情况,可以创建方法来分配正确值,然后将 OnDeserializingAttributeOnDeserializedAttribute 属性应用于该方法。

下面的示例演示类型上下文中的方法。 如果应用程序的较旧版本将 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 开始),如下例所示:

// 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

最佳做法

要确保版本管理行为正确,修改类型版本时请遵循以下规则:

  • 切勿移除已序列化的字段。

  • 如果未在以前版本中将 NonSerializedAttribute 属性应用于某个字段,则切勿将该属性应用于该字段。

  • 切勿更改已序列化字段的名称或类型。

  • 添加新的已序列化字段时,请应用 OptionalFieldAttribute 属性。

  • 从字段(在以前版本中不可序列化)中移除 NonSerializedAttribute 属性时,请应用 OptionalFieldAttribute 属性。

  • 对于所有可选字段,除非可接受 0 或 null 作为默认值,否则请使用序列化回调设置有意义的默认值。

要确保类型与将来的序列化引擎兼容,请遵循以下准则:

  • 始终正确设置 OptionalFieldAttribute 属性 (Attribute) 上的 VersionAdded 属性 (Property)。

  • 避免版本管理分支。

请参见

参考

SerializableAttribute
BinaryFormatter
SoapFormatter
VersionAdded
OptionalFieldAttribute
OnDeserializingAttribute
OnDeserializedAttribute
OnDeserializingAttribute
OnSerializedAttribute
StreamingContext
NonSerializedAttribute

其他资源

二进制序列化

Footer image

版权所有 (C) 2007 Microsoft Corporation。保留所有权利。