Types énumération dans les contrats de données

Les énumérations peuvent être exprimées dans le modèle de contrat de données. Cette rubrique décrit plusieurs exemples qui expliquent le modèle de programmation.

Principes de base de l'énumération

Appliquer l'attribut DataContractAttribute au type constitue une façon d'utiliser des types énumération dans le modèle de contrat de données. Vous devez appliquer ensuite l'attribut EnumMemberAttribute à chaque membre qui doit être inclus dans le contrat de données.

L'exemple suivant illustre deux classes. La première utilise l'énumération et la seconde définit l'énumération.

[DataContract]
public class Car
{
    [DataMember]
    public string model;
    [DataMember]
    public CarConditionEnum condition;
}

[DataContract(Name = "CarCondition")]
public enum CarConditionEnum
{
    [EnumMember]
    New,
    [EnumMember]
    Used,
    [EnumMember]
    Rental,
    Broken,
    Stolen
}
<DataContract()> _
Public Class Car
    <DataMember()> _
    Public model As String
    <DataMember()> _
    Public condition As CarConditionEnum
End Class

<DataContract(Name:="CarCondition")> _
Public Enum CarConditionEnum
    <EnumMember> NewCar
    <EnumMember> Used
    <EnumMember> Rental
    Broken
    Stolen
End Enum

Une instance de la classe Car peut être envoyée ou reçue uniquement si le champ condition a l'une des valeurs New, Used ou Rental. Si la condition a pour valeur Broken ou Stolen, une SerializationException est levée.

Vous pouvez utiliser comme d'habitude les propriétés DataContractAttribute (Name et Namespace) pour les contrats de données de l'énumération.

Valeurs de membre de l'énumération

En général, le contrat de données inclut des noms de membre de l'énumération, pas des valeurs numériques. Toutefois, lors de l'utilisation du modèle de contrat de données, si le côté réception est un client WCF, le schéma exporté conserve les valeurs numériques. Notez que ce n’est pas le cas lors de l’utilisation de la classe XmlSerializer.

Dans l'exemple précédent, si condition a la valeur Used et que les données sont sérialisées en XML, le XML résultant est <condition>Used</condition> et pas <condition>1</condition>. Par conséquent, le contrat de données suivant est équivalent au contrat de données de CarConditionEnum.

[DataContract(Name = "CarCondition")]
public enum CarConditionWithNumbers
{
    [EnumMember]
    New = 10,
    [EnumMember]
    Used = 20,
    [EnumMember]
    Rental = 30,
}
<DataContract(Name:="CarCondition")> _
Public Enum CarConditionWithNumbers
    <EnumMember> NewCar = 10
    <EnumMember> Used = 20
    <EnumMember> Rental = 30
End Enum

Par exemple, vous pouvez utiliser CarConditionEnum côté envoi et CarConditionWithNumbers côté réception. Même si le côté envoi utilise la valeur « 1 » pour Used et le côté réception la valeur « 20 », la représentation XML est <condition>Used</condition> pour les deux côtés.

Pour qu'il soit inclus dans le contrat de données, vous devez appliquer l'attribut EnumMemberAttribute. Dans le .NET Framework, vous pouvez toujours appliquer la valeur 0 (zéro) spéciale à une énumération, qui est également la valeur par défaut pour toute énumération. Cependant, même cette valeur zéro spéciale ne peut pas être sérialisée sauf si elle est marquée avec l'attribut EnumMemberAttribute.

Il existe deux exceptions :

  • Les énumérations d'indicateur (traitées plus loin dans cette rubrique).

  • Les membres de données de l'énumération dont la propriété EmitDefaultValue a la valeur false (auquel cas l'énumération avec la valeur zéro est omise des données sérialisées).

Personnalisation des valeurs de membre de l'énumération

Vous pouvez personnaliser la valeur de membre de l'énumération qui forme une partie du contrat de données en utilisant la propriété Value de l'attribut EnumMemberAttribute.

Par exemple, le contrat de données suivant est également équivalent au contrat de données de CarConditionEnum.

[DataContract(Name = "CarCondition")]
public enum CarConditionWithDifferentNames
{
    [EnumMember(Value = "New")]
    BrandNew,
    [EnumMember(Value = "Used")]
    PreviouslyOwned,
    [EnumMember]
    Rental
}
<DataContract(Name:="CarCondition")> _
Public Enum CarConditionWithDifferentNames
    <EnumMember(Value:="New")> BrandNew
    <EnumMember(Value:="Used")> PreviouslyOwned
    <EnumMember> Rental
End Enum

En cas de sérialisation, la valeur de PreviouslyOwned a la représentation XML <condition>Used</condition>.

Énumérations simples

Vous pouvez également sérialiser des types énumération auxquels l'attribut DataContractAttribute n'a pas été appliqué. Ces types énumération sont traités exactement comme indiqué précédemment, sauf que les membres (qui n'ont pas l'attribut NonSerializedAttribute) sont traités comme si l'attribut EnumMemberAttribute avait été appliqué. Par exemple, l'énumération suivante contient implicitement un contrat de données équivalent à l'exemple CarConditionEnum précédent.

public enum CarCondition
{
    New,
    Used,
    Rental,
    [NonSerialized]
    Lost
}
Public Enum CarCondition
    [New]
    Used
    Rental
End Enum

Vous pouvez utiliser des énumérations simples lorsque vous n'avez pas besoin de personnaliser le nom du contrat de données et l'espace de noms de l'énumération, ainsi que les valeurs de membre de l'énumération.

Note sur les énumérations simples

L'application de l'attribut EnumMemberAttribute aux énumérations simples n'a aucun effet.

L'application de l'attribut SerializableAttribute à l'énumération n'a aucun effet.

Le fait que la classe DataContractSerializer honore l'attribut NonSerializedAttribute appliqué aux membres de l'énumération est différent du comportement de BinaryFormatter et de SoapFormatter. Ces deux sérialiseurs ignorent l'attribut NonSerializedAttribute.

Énumérations d'indicateurs

Vous pouvez appliquer l'attribut FlagsAttribute aux énumérations. Dans ce cas, une liste de zéro ou plusieurs valeurs d'énumération peut être envoyée ou reçue simultanément.

Pour cela, appliquez l'attribut DataContractAttribute à l'énumération d'indicateur, puis marquez tous les membres qui sont à la puissance de deux avec l'attribut EnumMemberAttribute. Notez que pour utiliser une énumération d'indicateur, la progression doit être une séquence ininterrompue à la puissance de 2 (par exemple, 1, 2, 4, 8, 16, 32, 64).

Les étapes suivantes s'appliquent à l'envoi de la valeur d'énumération d'un indicateur :

  1. Essayez de rechercher un membre de l'énumération (auquel est appliqué l'attribut EnumMemberAttribute) qui correspond à la valeur numérique. Une fois trouvé, envoyez une liste qui contient seulement ce membre.

  2. Essayez de décomposer la valeur numérique en une somme de manière à obtenir des membres de l'énumération (à chacun desquels est appliqué l'attribut EnumMemberAttribute) qui correspondent à chaque partie de la somme. Envoyez la liste de tous ces membres. Notez que l'algorithme gourmand est utilisé pour rechercher cette somme, et donc il n'y a aucune garantie que la somme soit trouvée même si elle est présente. Pour éviter ce problème, assurez-vous que les valeurs numériques des membres de l'énumération sont à la puissance de deux.

  3. Si les deux étapes précédentes échouent et que la valeur numérique est différente de zéro, levez une SerializationException. Si la valeur numérique est égale à zéro, envoyez la liste vide.

Exemple

L'exemple d'énumération suivant peut être utilisé dans une opération d'indicateur.

[DataContract][Flags]
public enum CarFeatures
{
    None = 0,
    [EnumMember]
    AirConditioner = 1,
    [EnumMember]
    AutomaticTransmission = 2,
    [EnumMember]
    PowerDoors = 4,
    AlloyWheels = 8,
    DeluxePackage = AirConditioner | AutomaticTransmission | PowerDoors | AlloyWheels,
    [EnumMember]
    CDPlayer = 16,
    [EnumMember]
    TapePlayer = 32,
    MusicPackage = CDPlayer | TapePlayer,
    [EnumMember]
    Everything = DeluxePackage | MusicPackage
}
<DataContract(), Flags()> _
Public Enum CarFeatures
    None = 0
    <EnumMember> AirConditioner = 1
    <EnumMember> AutomaticTransmission = 2
    <EnumMember> PowerDoors = 4
    AlloyWheels = 8
    DeluxePackage = AirConditioner Or AutomaticTransmission Or PowerDoors Or AlloyWheels
    <EnumMember> CDPlayer = 16
    <EnumMember> TapePlayer = 32
    MusicPackage = CDPlayer Or TapePlayer
    <EnumMember> Everything = DeluxePackage Or MusicPackage
End Enum

Les valeurs de l'exemple suivant sont sérialisées comme indiqué.

CarFeatures cf1 = CarFeatures.AutomaticTransmission;
//Serialized as <cf1>AutomaticTransmission</cf1>

CarFeatures cf2 = (CarFeatures)5;
//Serialized as <cf2>AirConditioner PowerDoors</cf2> since 5=1+4

CarFeatures cf3 = CarFeatures.MusicPackage;
//Serialized as <cf3>CDPlayer TapePlayer</cf3> since MusicPackage itself is not an EnumMember

CarFeatures cf4 = CarFeatures.Everything;
//Serialized as <cf4>Everything</cf4> since Everything itself is an EnumMember

CarFeatures cf5 = CarFeatures.DeluxePackage;
//Throws a SerializationException since neither DeluxePackage nor AlloyWheels are EnumMembers

CarFeatures cf6 = CarFeatures.None;
//Serialized as the empty list <cf6></cf6> since there is no EnumMember mapped to zero
Private cf1 As CarFeatures = CarFeatures.AutomaticTransmission
'Serialized as <cf1>AutomaticTransmission</cf1>

Private cf2 As CarFeatures = ctype(5, CarFeatures)
'Serialized as <cf2>AirConditioner PowerDoors</cf2> since 5=1+4

Private cf3 As CarFeatures = CarFeatures.MusicPackage
'Serialized as <cf3>CDPlayer TapePlayer</cf3> since MusicPackage 
' itself is not an EnumMember.

Private cf4 As CarFeatures = CarFeatures.Everything
'Serialized as <cf4>Everything</cf4> since Everything itself is an EnumMember.

Private cf5 As CarFeatures = CarFeatures.DeluxePackage
'Throws a SerializationException since neither DeluxePackage nor 
' AlloyWheels are EnumMembers.

Private cf6 As CarFeatures = CarFeatures.None
'Serialized as the empty list <cf6></cf6> since there is no EnumMember mapped to zero.

Voir aussi