Независимая от версий сериализацияVersion tolerant serialization

В платформе .NET Framework версий 1.0 и 1.1 создание сериализуемых типов, которые можно повторно использовать от одной версии приложения к другой, было проблематичным.In version 1.0 and 1.1 of the .NET Framework, creating serializable types that would be reusable from one version of an application to the next was problematic. Если тип был изменен путем добавления дополнительных полей, возникала следующая проблема:If a type was modified by adding extra fields, the following problems would occur:

  • Приложения старых версий выдадут исключения при запросе десериализации новых версий старого типа.Older versions of an application would throw exceptions when asked to deserialize new versions of the old type.
  • Приложения более новых версий выдадут исключения при десериализации старых версий типа с отсутствующими данными.Newer versions of an application would throw exceptions when deserializing older versions of a type with missing data.

Независимая от версий сериализация (VTS) является набором функций, представленных в платформе .NET Framework 2.0, которые со временем упрощают изменение сериализуемых типов.Version Tolerant Serialization (VTS) is a set of features introduced in .NET Framework 2.0 that makes it easier, over time, to modify serializable types. В частности, функции VTS доступны для классов, к которым применен атрибут SerializableAttribute, включая универсальные типы.Specifically, the VTS features are enabled for classes to which the SerializableAttribute attribute has been applied, including generic types. VTS позволяет добавлять новые поля для таких классов, не нарушая совместимости с другими версиями типа.VTS makes it possible to add new fields to those classes without breaking compatibility with other versions of the type. Пример работающего приложения см. в разделе Образец технологии сериализации, независимой от версии.For a working sample application, see Version Tolerant Serialization Technology Sample.

Функции VTS доступны при использовании BinaryFormatter.The VTS features are enabled when using the BinaryFormatter. Кроме того, при использовании SoapFormatter доступны все функции, за исключением допустимости лишних данных.Additionally, all features except extraneous data tolerance are also enabled when using the SoapFormatter. Дополнительные сведения об использовании этих классов для сериализации см. в разделе Двоичная сериализация.For more information about using these classes for serialization, see Binary Serialization.

Предупреждение

Двоичная сериализация может быть опасной.Binary serialization can be dangerous. Никогда не выполняйте десериализацию данных из ненадежного источника и не пересылайте сериализируемые данные круговым путем через системы, которыми вы не управляете.Never deserialize data from an untrusted source and never round-trip serialized data to systems not under your control.

Список функцийFeature list

Набор функций включает в себя следующие:The set of features includes the following:

  • Допустимость лишних или непредвиденных данных.Tolerance of extraneous or unexpected data. Это позволяет новым версиям типа отправлять данные в старые версии.This enables newer versions of the type to send data to older versions.
  • Допустимость отсутствующих необязательных данных.Tolerance of missing optional data. Это позволяет старым версиям отправлять данные в новые версии.This enables older versions to send data to newer versions.
  • Обратные вызовы сериализации.Serialization callbacks. Это обеспечивает интеллектуальную настройку значения по умолчанию в случае отсутствия данных.This enables intelligent default value setting in cases where data is missing.

Кроме того, существует функция объявления при добавлении нового необязательного поля.In addition, there is a feature for declaring when a new optional field has been added. Это свойство VersionAdded атрибута OptionalFieldAttribute.This is the VersionAdded property of the OptionalFieldAttribute attribute.

Эти функции более подробно обсуждаются в следующих разделах.These features are discussed in greater detail in the following sections.

Допустимость лишних или непредвиденных данныхTolerance of extraneous or unexpected data

В прошлом при десериализации наличие каких-либо лишних или непредвиденных данных приводило к выводу исключения.In the past, during deserialization, any extraneous or unexpected data caused exceptions to be thrown. С использованием VTS в такой же ситуации какие-либо лишние или непредвиденные данные игнорируются, и исключения не выводятся.With VTS, in the same situation, any extraneous or unexpected data is ignored instead of causing exceptions to be thrown. Благодаря этому приложения, использующие более новые версии типа (т.е. версию с большим числом полей), могут отправлять информацию в приложения, ожидающие старые версии этого же типа.This enables applications that use newer versions of a type (that is, a version that includes more fields) to send information to applications that expect older versions of the same type.

В следующем примере дополнительные данные в CountryField версии 2.0 класса Address игнорируются, когда более старое приложение десериализует новую версию.In the following example, the extra data contained in the CountryField of version 2.0 of the Address class is ignored when an older application deserializes the newer version.

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

Допустимость отсутствующих данныхTolerance of missing data

Поля можно отметить как необязательные, применив к ним атрибут OptionalFieldAttribute.Fields can be marked as optional by applying the OptionalFieldAttribute attribute to them. При отсутствии во время десериализации необязательных данных модуль сериализации игнорирует такое отсутствие и не выводит исключение.During deserialization, if the optional data is missing, the serialization engine ignores the absence and does not throw an exception. Поэтому приложения, ожидающие старые версии типа, могут отправлять данные в приложения, ожидающие новые версии этого же типа.Thus, applications that expect older versions of a type can send data to applications that expect newer versions of the same type.

В следующем примере показана версия 2.0 класса Address с полем CountryField, отмеченным как необязательное.The following example shows version 2.0 of the Address class with the CountryField field marked as optional. Если приложение более старой версии отправляет версию 1 в новое приложение, ожидающее версии 2.0, отсутствие данных игнорируется.If an older application sends version 1 to a newer application that expects version 2.0, the absence of the data is ignored.

[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

Обратные вызовы сериализацииSerialization callbacks

Обратные вызовы сериализации являются механизмом, который предоставляет обработчики для процесса сериализации и десериализации в четырех точках.Serialization callbacks are a mechanism that provides hooks into the serialization/deserialization process at four points.

АтрибутAttribute Когда вызывается связанный методWhen the Associated Method is Called Типичные случаи использованияTypical Use
OnDeserializingAttribute Перед десериализацией.*Before deserialization.* Инициализация значений по умолчанию для необязательных полей.Initialize default values for optional fields.
OnDeserializedAttribute После десериализации.After deserialization. Исправление значений необязательных полей на основании содержимого других полей.Fix optional field values based on contents of other fields.
OnSerializingAttribute Перед сериализацией.Before serialization. Подготовка к сериализации.Prepare for serialization. Например, создание структур необязательных данных.For example, create optional data structures.
OnSerializedAttribute После сериализации.After serialization. Регистрация событий сериализации в журнале.Log serialization events.

* Этот обратный вызов осуществляется перед конструктором десериализации, если таковой имеется.* This callback is invoked before the deserialization constructor, if one is present.

Использование обратных вызововUsing callbacks

Чтобы использовать обратные вызовы, примените соответствующий атрибут к методу, который принимает параметр StreamingContext.To use callbacks, apply the appropriate attribute to a method that accepts a StreamingContext parameter. Для каждого из этих атрибутов можно отметить только один метод для класса.Only one method per class can be marked with each of these attributes. Например:For example:

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

Эти методы предназначены для управления версиями.The intended use of these methods is for versioning. Во время десериализации возможна неправильная инициализация необязательного поля, если данные для него отсутствуют.During deserialization, an optional field may not be correctly initialized if the data for the field is missing. Это можно исправить, создав метод, который назначает правильное значение, а затем применяет к методу атрибут OnDeserializingAttribute или OnDeserializedAttribute.This can be corrected by creating the method that assigns the correct value, then applying either the OnDeserializingAttribute or OnDeserializedAttribute attribute to the method.

В следующем примере показан метод в контексте типа.The following example shows the method in the context of a type. Если приложение более ранней версии отправляет экземпляр класса Address в приложение более поздней версии, данные поля CountryField будут отсутствовать.If an earlier version of an application sends an instance of the Address class to a later version of the application, the CountryField field data will be missing. Но после десериализации полю будет присвоено значение по умолчанию «Япония».But after deserialization, the field will be set to a default value "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(sc As StreamingContext)
        CountryField = "Japan"
    End Sub
End Class

Свойство VersionAddedThe VersionAdded property

OptionalFieldAttribute имеет свойство VersionAdded.The OptionalFieldAttribute has the VersionAdded property. В платформе .NET Framework версии 2.0 это свойство не используется.In version 2.0 of the .NET Framework, this isn't used. Однако важно правильно задать это свойство, чтобы обеспечить совместимость типа с будущими модулями сериализации.However, it's important to set this property correctly to ensure that the type will be compatible with future serialization engines.

Свойство содержит сведения о том, какую версию типа добавило указанное поле.The property indicates which version of a type a given field has been added. Увеличение должно осуществляться точно по одному (начиная с 2) при каждом изменении типа, см. следующий пример:It should be incremented by exactly one (starting at 2) every time the type is modified, as shown in the following example:

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

SerializationBinderSerializationBinder

Некоторым пользователям может понадобиться выбирать классы для сериализации и десериализации, поскольку на сервере и клиенте требуются разные версии класса.Some users may need to control which class to serialize and deserialize because a different version of the class is required on the server and client. Класс SerializationBinder является абстрактным классом, который используется для управления фактическими типами, применяемыми при сериализации и десериализации.SerializationBinder is an abstract class used to control the actual types used during serialization and deserialization. Чтобы использовать этот класс, создайте класс, производный от SerializationBinder, и переопределите методы BindToName и BindToType.To use this class, derive a class from SerializationBinder and override the BindToName and BindToType methods. Дополнительные сведения см. в разделе Управление сериализацией и десериализацией с помощью помощью SerializationBinder.For more information, see Controlling Serialization and Deserialization with SerializationBinder.

РекомендацииBest practices

Чтобы обеспечить правильное управление версиями, соблюдайте следующие правила при изменении типа от версии к версии:To ensure proper versioning behavior, follow these rules when modifying a type from version to version:

  • Никогда не удаляйте сериализованное поле.Never remove a serialized field.
  • Никогда не применяйте атрибут NonSerializedAttribute к полю, если атрибут не был применен к полю в предыдущей версии.Never apply the NonSerializedAttribute attribute to a field if the attribute was not applied to the field in the previous version.
  • Никогда не изменяйте имя или тип сериализованного поля.Never change the name or the type of a serialized field.
  • При добавлении нового сериализованного поля применяйте атрибут OptionalFieldAttribute.When adding a new serialized field, apply the OptionalFieldAttribute attribute.
  • При удалении атрибута NonSerializedAttribute из поля (которое было несериализуемым в предыдущей версии) примените атрибут OptionalFieldAttribute.When removing a NonSerializedAttribute attribute from a field (that was not serializable in a previous version), apply the OptionalFieldAttribute attribute.
  • Для всех необязательных полей присвойте значимые значения по умолчанию с помощью обратных вызовов сериализации, если 0 или null приемлемы в качестве значений по умолчанию.For all optional fields, set meaningful defaults using the serialization callbacks unless 0 or null as defaults are acceptable.

Чтобы гарантировать совместимость типа с будущими модулями сериализации, соблюдайте следующие правила:To ensure that a type will be compatible with future serialization engines, follow these guidelines:

  • Всегда правильно задавайте свойство VersionAdded для атрибута OptionalFieldAttribute.Always set the VersionAdded property on the OptionalFieldAttribute attribute correctly.
  • Избегайте ветвления версий.Avoid branched versioning.

См. также:See also