Sérialisation personnalisée
La sérialisation personnalisée est le processus visant à contrôler la sérialisation et la désérialisation d'un type. En contrôlant la sérialisation, il est possible de garantir la compatibilité de sérialisation qui permet de procéder à une sérialisation et à une désérialisation entre les versions d'un type sans nuire aux fonctionnalités principales du type. Par exemple, la première version d'un type peut comporter uniquement deux champs. Dans la version suivante d'un type, plusieurs autres champs sont ajoutés. Pourtant la seconde version d'une application doit être capable de sérialiser et désérialiser les deux types. Les sections suivantes expliquent comment contrôler la sérialisation.
Exécution de méthodes personnalisées pendant et après la sérialisation
La méthode conseillée et la plus facile (introduite dans la version 2.0 de .Net Framework) consiste à appliquer les attributs suivants aux méthodes utilisées pour corriger des données pendant et après la sérialisation :
Ces attributs permettent au type de prendre part à l'une des phases, ou aux quatre phases, des processus de sérialisation et de désérialisation. Les attributs spécifient les méthodes du type qui doivent être appelées au cours de chaque phase. Les méthodes n'accèdent pas au flux de sérialisation, mais elles permettent de modifier l'objet avant et après la sérialisation ou avant et après la désérialisation. Les attributs peuvent être appliqués à tous les niveaux de la hiérarchie d'héritage de types, et chaque méthode est appelée dans la hiérarchie en commençant par les types de base aux types les plus dérivés. Ce mécanisme permet d'éviter les difficultés et les problèmes liés à l'implémentation de l'interface ISerializable en chargeant l'implémentation la plus dérivée de l'exécution de la sérialisation et de la désérialisation. En outre, ce mécanisme permet aux formateurs d'ignorer le remplissage de champs et l'extraction du flux de sérialisation. Pour plus d'informations et d'exemples sur le contrôle de la sérialisation et de la désérialisation, cliquez sur l'un des liens ci-dessus.
De plus, lorsque vous ajoutez un nouveau champ à un type sérialisable existant, appliquez l'attribut OptionalFieldAttribute au champ. BinaryFormatter et SoapFormatter ignorent l'absence du champ lors du traitement d'un flux auquel il manque le nouveau champ.
Implémentation de l'interface ISerializable
Pour contrôler la sérialisation, une autre méthode consiste à implémenter l'interface ISerializable sur un objet. Notez toutefois que la méthode présentée dans la section précédente prime cette méthode de contrôle de la sérialisation.
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 la méthode GetObjectData est appelée pendant la sérialisation, vous êtes chargé de remplir l'objet 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 à l'objet 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 l'interface 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 sont 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 l'autorisation 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 la méthode 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, voir 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 n'est envoyé si le constructeur est absent et une exception est 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 en 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 l'autorisation 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 montre comment cela est réalisé avec la classe MyObject
décrite 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. Sinon, le constructeur sur la classe de base n'est jamais appelé et l'objet n'est pas entièrement construit au terme de la désérialisation.
Les objets sont totalement reconstruits et l'appel des 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 l'ensemble du graphique d'objet est désérialisé. À 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. 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
Concepts
Autres ressources
Sérialisation binaire
Objets distants
Sérialisation XML et SOAP
Sécurité d'accès du code