Partager via


Sérialisation personnalisée

Vous pouvez personnaliser le processus de sérialisation en implémentant l'interface ISerializable sur un objet. Ceci est particulièrement utile dans les situations où la valeur d'une variable de membre est non valide après la désérialisation, mais vous devez fournir la variable avec une valeur afin de reconstruire l'état complet de l'objet. En outre, vous ne devez pas utiliser la sérialisation par défaut sur une classe marquée avec l'attribut Serializable et dotée d'une sécurité déclarative ou impérative au niveau de la classe ou sur ses constructeurs. Ces classes doivent en revanche toujours implémenter l'interface ISerializable.

L'implémentation de l'interface ISerializable implique l'implémentation de la méthode GetObjectData et d'un constructeur spécial qui est utilisé lors de la désérialisation de l'objet. L'exemple de code ci-dessous illustre l'implémentation de l'interface ISerializable sur la classe MyObject à partir d'une section précédente.

[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);
  }
}

Lorsque GetObjectData est appelée pendant la sérialisation, vous êtes chargé de remplir le SerializationInfo fourni avec l'appel de méthode. Ajoutez simplement les variables à sérialiser comme paires nom-valeur. Tout texte peut être utilisé comme nom. Vous avez la liberté de décider si les variables de membre sont ajoutées à SerializationInfo, pourvu que les données suffisantes soient sérialisées pour restaurer l'objet pendant la désérialisation. Les classes dérivées doivent appeler la méthode GetObjectData sur l'objet de base si ce dernier implémente ISerializable.

Notez que la sérialisation peut permettre à un autre code de voir ou de modifier les données de l'instance d'objet qui seraient autrement inaccessibles. Le code effectuant la sérialisation exige donc l'autorisation SecurityPermission avec l'indicateur SerializationFormatter spécifié. 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. La méthode GetObjectData doit être explicitement protégée soit par la demande de SecurityPermission avec l'indicateur SerializationFormatter spécifié, soit par la demande d'autres autorisations qui protègent spécifiquement les données privées.

Si un champ privé stocke des informations sensibles, vous devez demander les autorisations appropriées sur GetObjectData pour protéger ces données. Rappelez-vous que le code qui a reçu l'autorisation SecurityPermission avec l'indicateur SerializationFormatter spécifié peut afficher et modifier les données stockées dans les champs privés. Un appelant malveillant disposant de l'autorisation SecurityPermission peut donc afficher des données telles que l'emplacement des répertoires cachés ou les autorisations octroyées et utiliser ces données pour exploiter une faille de sécurité sur l'ordinateur. Pour avoir une liste exhaustive des indicateurs d'autorisation de sécurité que vous pouvez spécifier, consultez SecurityPermissionFlag, énumération.

Il est important de souligner que lorsque l'interface ISerializable est ajoutée à une classe, vous devez implémenter GetObjectData ainsi que le constructeur spécial. Le compilateur vous avertit si la méthode GetObjectData est manquante. Toutefois, dans la mesure où il est impossible d'appliquer l'implémentation d'un constructeur, aucun avertissement ne sera envoyé si le constructeur est absent et une exception sera levée en cas de tentative de désérialisation d'une classe sans constructeur.

Le design actuel a eu la préférence sur une méthode SetObjectData pour contourner les problèmes éventuels de sécurité et de versioning. Par exemple, une méthode SetObjectData doit être publique si elle est définie comme faisant partie d'une interface ; les utilisateurs doivent donc écrire du code pour se protéger contre de multiples appels à la méthode SetObjectData. Sinon, une application nuisible appelant la méthode SetObjectData sur un objet en cours d'exécution d'une opération peut provoquer des problèmes.

Pendant la désérialisation, SerializationInfo est passé à la classe utilisant le constructeur fourni à cet effet. Les contraintes de visibilité placées sur le 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. Toutefois, la meilleure pratique consiste à protéger le constructeur sauf si la classe est scellée, auquel cas le constructeur doit être marqué comme privé. Le constructeur doit également effectuer une validation complète de l'entrée. Pour éviter un usage impropre par du code nuisible, le constructeur doit appliquer les mêmes contrôles de sécurité et les mêmes autorisations que ceux requis pour obtenir une instance de la classe à l'aide de tout autre constructeur. Si vous ne suivez pas ce conseil, du code nuisible peut présérialiser un objet, obtenir le contrôle à l'aide de SecurityPermission avec l'indicateur SerializationFormatter spécifié et désérialiser l'objet sur un ordinateur client passant ainsi outre la sécurité qui aurait été appliquée dans le cadre d'une construction d'instance standard à l'aide d'un constructeur public.

Pour restaurer l'état de l'objet, extrayez simplement les valeurs des variables de SerializationInfo à l'aide des noms utilisés pendant la sérialisation. 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.

Lorsque vous dérivez une nouvelle classe d'une classe qui implémente ISerializable, la classe dérivée doit implémenter le constructeur ainsi que la méthode GetObjectData si elle a des variables qui doivent être sérialisées. L'exemple de code ci-dessous illustre cela à l'aide de la classe MyObject illustrée précédemment.

[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);
  }
}

N'oubliez pas d'appeler la classe de base dans le constructeur de désérialisation ; si cela n'est pas fait, le constructeur sur la classe de base ne sera jamais appelé et l'objet ne sera pas totalement construit après la désérialisation.

Les objets sont totalement reconstruits et l'appel de méthodes pendant la désérialisation peut avoir des effets secondaires indésirables, parce que les méthodes appelées peuvent se référer à des références d'objets qui n'ont pas été désérialisés au moment de l'appel. Si la classe désérialisée implémente IDeserilizationCallback, la méthode OnDeserialization est automatiquement appelée une fois que la totalité du graphique d'objet est désérialisée. À ce stade, tous les objets enfants référencés ont été totalement restaurés. Une table de hachage est un exemple classique de classe difficile à désérialiser sans faire appel à l'écouteur d'événements décrit ci-dessus. Il est aisé d'extraire les paires clé-valeur pendant la désérialisation, mais le rajout de ces objets à la table de hachage peut poser des problèmes, parce que la désérialisation des classes dérivées de la table de hachage n'est pas garantie. L'appel de méthodes sur une table de hachage n'est donc pas conseillé à ce stade.

Voir aussi

Sérialisation binaire | Accès aux objets dans d'autres domaines d'application à l'aide de .NET Remoting | Sérialisation XML et SOAP | Sécurité et sérialisation | Sécurité d'accès du code