Sérialisation personnaliséeCustom serialization

La sérialisation personnalisée est le processus de contrôle de la sérialisation et désérialisation d'un type.Custom serialization is the process of controlling the serialization and deserialization of a type. En contrôlant la sérialisation, il est possible de garantir la compatibilité de sérialisation (capacité de sérialisation et désérialisation entre des versions d’un type) sans interrompre la fonctionnalité principale du type.By controlling serialization, it's possible to ensure serialization compatibility, which is the ability to serialize and deserialize between versions of a type without breaking the core functionality of the type. Par exemple, il peut y avoir uniquement deux champs dans la première version d'un type.For example, in the first version of a type, there may be only two fields. Dans la version suivante d'un type, plusieurs champs supplémentaires sont ajoutés.In the next version of a type, several more fields are added. La deuxième version d'une application doit néanmoins être en mesure de sérialiser et désérialiser les deux types.Yet the second version of an application must be able to serialize and deserialize both types. Les sections suivantes décrivent comment contrôler la sérialisation.The following sections describe how to control serialization.

Avertissement

La sérialisation binaire peut être dangereuse.Binary serialization can be dangerous. Pour plus d’informations, consultez le Guide de la sécurité BinaryFormatter.For more information, see BinaryFormatter security guide.

Important

Dans les versions antérieures au .NET Framework 4.0, la sérialisation de données utilisateur personnalisées dans un assembly d’un niveau de confiance partiel était obtenue à l’aide de GetObjectData.In versions previous to .NET Framework 4.0, serialization of custom user data in a partially trusted assembly was accomplished using the GetObjectData. Depuis la version 4.0, cette méthode est marquée avec l'attribut SecurityCriticalAttribute, ce qui empêche l'exécution dans les assemblys d'un niveau de confiance partiel.Starting with version 4.0, that method is marked with the SecurityCriticalAttribute attribute which prevents execution in partially trusted assemblies. Pour contourner cette condition, implémentez l'interface ISafeSerializationData.To work around this condition, implement the ISafeSerializationData interface.

Exécution de méthodes personnalisées pendant et après la sérialisationRunning custom methods during and after serialization

La méthode conseillée la plus simple (présentée dans la version 2.0 du .NET Framework) consiste à appliquer les attributs suivants aux méthodes utilisées pour corriger des données pendant et après la sérialisation :The best practice and easiest way (introduced in version 2.0 of the .NET Framework) is to apply the following attributes to methods that are used to correct data during and after serialization:

Ces attributs permettent au type de participer à l'une ou à l'ensemble des quatre phases des processus de sérialisation et de désérialisation.These attributes allow the type to participate in any one of, or all four of the phases, of the serialization and deserialization processes. Les attributs spécifient les méthodes du type qui doivent être invoquées pendant chaque phase.The attributes specify the methods of the type that should be invoked during each phase. Les méthodes n'accèdent pas au flux de données de sérialisation mais vous permettent en revanche de modifier l'objet avant et après la sérialisation, ou avant et après la désérialisation.The methods do not access the serialization stream but instead allow you to alter the object before and after serialization, or before and after deserialization. Les attributs peuvent être appliqués à tous les niveaux de la hiérarchie d'héritage de type, et chaque méthode est appelée dans la hiérarchie de la base au plus dérivé.The attributes can be applied at all levels of the type inheritance hierarchy, and each method is called in the hierarchy from the base to the most derived. Ce mécanisme évite la complexité et tous les problèmes résultant de l'implémentation de l'interface ISerializable en rendant l'implémentation la plus dérivée responsable de la sérialisation et de la désérialisation.This mechanism avoids the complexity and any resulting issues of implementing the ISerializable interface by giving the responsibility for serialization and deserialization to the most derived implementation. En outre, ce mécanisme permet aux formateurs d'ignorer le remplissage de champs et la récupération du flux de données de sérialisation.Additionally, this mechanism allows the formatters to ignore the population of fields and retrieval from the serialization stream. Pour obtenir des informations et des exemples concernant le contrôle de la sérialisation et de la désérialisation, cliquez sur l'un des liens précédents.For details and examples of controlling serialization and deserialization, click any of the previous links.

De plus, lorsque vous ajoutez un nouveau champ à un type sérialisable existant, appliquez l'attribut OptionalFieldAttribute au champ.In addition, when adding a new field to an existing serializable type, apply the OptionalFieldAttribute attribute to the field. BinaryFormatter et SoapFormatter ignorent l'absence du champ lorsqu'un flux de données dont le nouveau champ est manquant est traité.The BinaryFormatter and the SoapFormatter ignores the absence of the field when a stream that is missing the new field is processed.

Implémentation de l’interface ISerializableImplementing the ISerializable interface

L’autre méthode permettant de contrôler la sérialisation s’effectue en implémentant l’interface ISerializable sur un objet.The other way to control serialization is achieved by implementing the ISerializable interface on an object. Toutefois, notez que la méthode de la section précédente remplace cette méthode pour contrôler la sérialisation.Note, however, that the method in the previous section supersedes this method to control serialization.

De plus, vous ne devez pas utiliser la sérialisation par défaut sur une classe marquée avec l’attribut Serializable et présentant une sécurité déclarative ou impérative au niveau de la classe ou sur ses constructeurs.In addition, you should not use default serialization on a class that is marked with the Serializable attribute and has declarative or imperative security at the class level or on its constructors. En revanche, ces classes doivent toujours implémenter l'interface ISerializable.Instead, these classes should always implement the ISerializable interface.

L’implémentation de ISerializable implique l’implémentation de la méthode GetObjectData et l’utilisation d’un constructeur spécial quand l’objet est désérialisé.Implementing ISerializable involves implementing the GetObjectData method and a special constructor that is used when the object is deserialized. L'exemple de code suivant indique comment implémenter ISerializable sur la classe MyObject d'une section précédente.The following sample code shows how to implement ISerializable on the MyObject class from a previous section.

[Serializable]
public class MyObject : ISerializable
{
    public int n1;
    public int n2;
    public String str;

    public MyObject()
    {
    }

    protected MyObject(SerializationInfo info, StreamingContext context)
    {
      n1 = info.GetInt32("i");
      n2 = info.GetInt32("j");
      str = info.GetString("k");
    }

    [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
    public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("i", n1);
        info.AddValue("j", n2);
        info.AddValue("k", str);
    }
}
<Serializable()>  _
Public Class MyObject
    Implements ISerializable
    Public n1 As Integer
    Public n2 As Integer
    Public str As String

    Public Sub New()
    End Sub

    Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
        n1 = info.GetInt32("i")
        n2 = info.GetInt32("j")
        str = info.GetString("k")
    End Sub 'New

    <SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter := True)> _
    Public Overridable Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext)
        info.AddValue("i", n1)
        info.AddValue("j", n2)
        info.AddValue("k", str)
    End Sub
End Class

Quand GetObjectData est appelé pendant la sérialisation, vous êtes chargé de remplir le SerializationInfo fourni avec l’appel à méthode.When GetObjectData is called during serialization, you are responsible for populating the SerializationInfo provided with the method call. Ajoutez les variables à sérialiser sous forme de paires de noms et de valeurs.Add the variables to be serialized as name and value pairs. Tout texte peut être utilisé comme nom.Any text can be used as the name. Vous pouvez décider quelles variables membres sont ajoutées au SerializationInfo, à condition qu'il y ait suffisamment de données sérialisées pour restaurer l'objet pendant la désérialisation.You have the freedom to decide which member variables are added to the SerializationInfo, provided that sufficient data is serialized to restore the object during deserialization. Les classes dérivées doivent appeler la méthode GetObjectData sur l’objet de base si ce dernier implémente ISerializable.Derived classes should call the GetObjectData method on the base object if the latter implements ISerializable.

Notez que la sérialisation peut permettre à un autre code d'afficher ou de modifier des données d'instance d'objet qui seraient autrement inaccessibles.Note that serialization can allow other code to see or modify object instance data that is otherwise inaccessible. Par conséquent, le code qui exécute la sérialisation exige SecurityPermission avec l’indicateur SerializationFormatter défini.Therefore, code that performs serialization requires the SecurityPermission with the SerializationFormatter flag specified. Dans le cadre de la stratégie par défaut, cette autorisation n'est pas accordée à du code téléchargé depuis Internet ou un intranet ; seul le code sur l'ordinateur local reçoit cette autorisation.Under default policy, this permission is not given to Internet-downloaded or intranet code; only code on the local computer is granted this permission. La méthode GetObjectData doit être protégée explicitement en exigeant SecurityPermission avec l’indicateur SerializationFormatter défini ou en exigeant d’autres autorisations qui permettent de protéger spécifiquement des données privées.The GetObjectData method must be explicitly protected either by demanding the SecurityPermission with the SerializationFormatter flag specified or by demanding other permissions that specifically help protect private data.

Si un champ privé stocke des informations sensibles, vous devez exiger les autorisations appropriées sur GetObjectData pour protéger les données.If a private field stores sensitive information, you should demand the appropriate permissions on GetObjectData to protect the data. Rappelez-vous que le code auquel a été attribuée l’autorisation SecurityPermission avec l’indicateur SerializationFormatter défini peut afficher et modifier les données stockées dans les champs privés.Remember that code that has been granted SecurityPermission with the SerializationFormatter flag specified can view and modify the data stored in private fields. Un appelant malveillant qui a obtenu l’autorisation SecurityPermission peut consulter des données telles que les emplacements de répertoire cachés ou les autorisations accordées, et utiliser ces données pour exploiter une faille de sécurité sur l’ordinateur.A malicious caller granted this SecurityPermission can view data such as hidden directory locations or granted permissions and use the data to exploit a security vulnerability on the computer. Pour obtenir une liste complète des indicateurs d’autorisation de sécurité que vous pouvez spécifier, consultez l’énumération SecurityPermissionFlag.For a complete list of the security permission flags you can specify, see the SecurityPermissionFlag Enumeration.

Il convient de souligner que quand ISerializable est ajouté à une classe, vous devez implémenter GetObjectData et le constructeur spécial.It's important to stress that when ISerializable is added to a class you must implement both GetObjectData and the special constructor. Le compilateur vous avertit si GetObjectData est manquant.The compiler warns you if GetObjectData is missing. Toutefois, parce qu'il est impossible de mettre en œuvre l'implémentation d'un constructeur, aucun avertissement n'est fourni si le constructeur est absent, et une exception est levée lorsqu'une tentative est faite pour désérialiser une classe sans le constructeur.However, because it is impossible to enforce the implementation of a constructor, no warning is provided if the constructor is absent, and an exception is thrown when an attempt is made to deserialize a class without the constructor.

La conception actuelle a priorité sur une méthode SetObjectData pour contourner les problèmes potentiels de sécurité et de versioning.The current design was favored above a SetObjectData method to get around potential security and versioning problems. Par exemple, une méthode SetObjectData doit être publique si elle est définie dans le cadre d’une interface. Ainsi, les utilisateurs doivent écrire du code pour éviter que la méthode SetObjectData soit appelée plusieurs fois.For example, a SetObjectData method must be public if it is defined as part of an interface; thus users must write code to defend against having the SetObjectData method called multiple times. Sinon, une application malveillante qui appelle la méthode SetObjectData sur un objet lors de l’exécution d’une opération peut provoquer des problèmes potentiels.Otherwise, a malicious application that calls the SetObjectData method on an object in the process of executing an operation can cause potential problems.

Pendant la désérialisation, SerializationInfo est transmis à la classe à l'aide du constructeur prévu à cette fin.During deserialization, SerializationInfo is passed to the class using the constructor provided for this purpose. Toutes les contraintes de visibilité imposées au constructeur sont ignorées lorsque l'objet est désérialisé. Vous pouvez donc marquer la classe comme publique, protégée, interne ou privée.Any visibility constraints placed on the constructor are ignored when the object is deserialized; so you can mark the class as public, protected, internal, or private. Toutefois, la méthode conseillée consiste à protéger le constructeur tant que la classe n'est pas scellée. Le cas échéant, le constructeur doit être marqué comme privé.However, it is a best practice to make the constructor protected unless the class is sealed, in which case the constructor should be marked private. Le constructeur doit également exécuter une validation de saisie complète.The constructor should also perform thorough input validation. Pour éviter une utilisation incorrecte par un code malveillant, le constructeur doit appliquer les mêmes vérifications de sécurité et autorisations nécessaires pour obtenir une instance de la classe utilisant tout autre constructeur.To avoid misuse by malicious code, the constructor should enforce the same security checks and permissions required to obtain an instance of the class using any other constructor. Si vous ne respectez pas cette recommandation, le code malveillant peut présérialiser un objet, obtenir le contrôle avec SecurityPermission avec l’indicateur SerializationFormatter défini, et désérialiser l’objet sur un ordinateur client en contournant toute sécurité qui aurait été appliquée pendant la construction d’instance standard à l’aide d’un constructeur public.If you do not follow this recommendation, malicious code can preserialize an object, obtain control with the SecurityPermission with the SerializationFormatter flag specified and deserialize the object on a client computer bypassing any security that would have been applied during standard instance construction using a public constructor.

Pour restaurer l'état de l'objet, récupérez simplement les valeurs des variables de SerializationInfo à l'aide des noms utilisés pendant la sérialisation.To restore the state of the object, simply retrieve the values of the variables from SerializationInfo using the names used during serialization. Si la classe de base implémente ISerializable, le constructeur de base doit être appelé pour permettre à l'objet de base de restaurer ses variables.If the base class implements ISerializable, the base constructor should be called to allow the base object to restore its variables.

Quand vous dérivez une nouvelle classe d’une classe qui implémente ISerializable, la classe dérivée doit implémenter aussi bien le constructeur que la méthode GetObjectData si elle a des variables qui doivent être sérialisées.When you derive a new class from one that implements ISerializable, the derived class must implement both the constructor as well as the GetObjectData method if it has variables that need to be serialized. L'exemple de code suivant illustre la procédure à l'aide de la classe MyObject montrée précédemment.The following code example shows how this is done using the MyObject class shown previously.

[Serializable]
public class ObjectTwo : MyObject
{
    public int num;

    public ObjectTwo()
      : base()
    {
    }

    protected ObjectTwo(SerializationInfo si, StreamingContext context)
      : base(si, context)
    {
        num = si.GetInt32("num");
    }

    [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
    public override void GetObjectData(SerializationInfo si, StreamingContext context)
    {
        base.GetObjectData(si,context);
        si.AddValue("num", num);
    }
}
<Serializable()>  _
Public Class ObjectTwo
    Inherits MyObject
    Public num As Integer

    Public Sub New()

    End Sub

    Protected Sub New(ByVal si As SerializationInfo, _
    ByVal context As StreamingContext)
        MyBase.New(si, context)
        num = si.GetInt32("num")
    End Sub

    <SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter := True)> _
    Public Overrides Sub GetObjectData(ByVal si As SerializationInfo, ByVal context As StreamingContext)
        MyBase.GetObjectData(si, context)
        si.AddValue("num", num)
    End Sub
End Class

N’oubliez pas d’appeler la classe de base dans le constructeur de désérialisation.Don't forget to call the base class in the deserialization constructor. Sinon, le constructeur de la classe de base n’est jamais appelé et l’objet n’est pas entièrement construit après la désérialisation.If this isn't done, the constructor on the base class is never called, and the object is not fully constructed after deserialization.

Les objets sont reconstruits à l'envers et les méthodes d'appel au cours de la désérialisation peuvent avoir des effets secondaires indésirables, car les méthodes appelées peuvent se rapporter aux références d'objet qui n'ont pas été désérialisées au moment de l'appel.Objects are reconstructed from the inside out; and calling methods during deserialization can have undesirable side effects, because the methods called might refer to object references that have not been deserialized by the time the call is made. Si la classe qui est désérialisée implémente IDeserializationCallback, la méthode OnDeserialization est appelée automatiquement une fois le graphique d’objets entier désérialisé.If the class being deserialized implements the IDeserializationCallback, the OnDeserialization method is automatically called when the entire object graph has been deserialized. À ce stade, tous les objets enfants référencés ont été restaurés complètement.At this point, all the child objects referenced have been fully restored. Une table de hachage est un exemple typique d'une classe difficile à désérialiser sans utiliser l'écouteur d'événements.A hash table is a typical example of a class that is difficult to deserialize without using the event listener. Il est facile de récupérer les clés et valeurs paires pendant la désérialisation, mais l'ajout de ces objets à la table de hachage peut provoquer des problèmes. En effet, il n'y a aucune garantie que les classes dérivées de la table de hachage sont désérialisées.It is easy to retrieve the key and value pairs during deserialization, but adding these objects back to the hash table can cause problems, because there is no guarantee that classes that derived from the hash table have been deserialized. Par conséquent, les méthodes d'appel sur une table de hachage ne sont pas recommandées à ce stade.Calling methods on a hash table at this stage is therefore not advisable.

Voir aussiSee also