Enumerationstypen in Datenverträgen

Enumerationen können im Datenvertragsmodell ausgedrückt werden. In diesem Thema werden mehrere Beispiele behandelt, in denen das Programmiermodell erklärt wird.

Grundlagen der Enumeration

Eine Möglichkeit, Enumerationstypen im Datenvertragmodell zu verwenden, besteht darin, das DataContractAttribute-Attribut auf den Typ anzuwenden. Anschließend müssen Sie das EnumMemberAttribute-Attribut auf jeden Member anwenden, der im Datenvertrag enthalten sein soll.

Das folgende Beispiel zeigt zwei Klassen. Die erste Klasse verwendet die Enumeration, und die zweite Klasse definiert die Enumeration.

[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

Sie können eine Instanz der Car-Klasse nur senden oder empfangen, wenn das Feld condition auf einen der folgenden Werte festgelegt ist: New, Used oder Rental. Wenn condition den Wert Broken oder Stolen hat, wird eine SerializationException ausgelöst.

Sie können die DataContractAttribute-Eigenschaften (Name und Namespace) wie gewohnt für Enumerationsdatenverträge verwenden.

Enumerationsmemberwerte

Im Allgemeinen enthält der Datenvertrag Enumerationsmembernamen, keine numerischen Werte. Wenn Sie jedoch das Datenvertragsmodell verwenden, werden die numerischen Werte im exportierten Schema beibehalten, wenn es sich bei der empfangenden Stelle um einen WCF-Client handelt. Beachten Sie, dass dies nicht der Fall ist, wenn Sie die XmlSerializer-Klasse verwenden.

Wenn im obigen Beispiel condition auf Used festgelegt ist und die Daten in XML serialisiert werden, lautet das XML-Ergebnis <condition>Used</condition><condition>1</condition>, und nicht . Deshalb entspricht der folgende Datenvertrag dem Datenvertrag von 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

Sie können beispielsweise CarConditionEnum auf der Absenderseite und CarConditionWithNumbers auf der Empfängerseite verwenden. Obwohl die Absenderseite den Wert 1 und die Empfängerseite den Wert 20 für Used verwendet, lautet die XML-Darstellung für beide Seiten <condition>Used</condition>.

Um das Einbinden in den Datenvertrag zu erreichen, müssen Sie das EnumMemberAttribute-Attribut anwenden. In .NET Framework können Sie immer den Sonderwert 0 (null) auf eine Enumeration anwenden. Dabei handelt es sich auch um den Standardwert für Enumerationen. Auch dieser Sonderwert 0 kann jedoch nur serialisiert werden, wenn er mithilfe des EnumMemberAttribute-Attributs gekennzeichnet ist.

Dabei gelten zwei Ausnahmen:

  • Flagenumerationen (weiter unten in diesem Thema beschrieben).

  • Enumerationsdatenmember, für die die EmitDefaultValue-Eigenschaft auf false festgelegt ist. (In diesem Fall wird die Enumeration mit dem Wert "0" einfach aus den serialisierten Daten weggelassen.)

Anpassen von Enumerationsmemberwerten

Sie können den Enumerationsmemberwert anpassen, der Teil des Datenvertrags ist, indem Sie die Value-Eigenschaft des EnumMemberAttribute-Attributs verwenden.

Der folgende Datenvertrag entspricht auch dem Datenvertrag von 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

Nach der Serialisierung weist der Wert von PreviouslyOwned die XML-Darstellung <condition>Used</condition> auf.

Einfache Enumerationen

Sie können auch Enumerationstypen serialisieren, auf die das DataContractAttribute-Attribut nicht angewendet wurde. Enumerationstypen dieser Art werden exakt wie zuvor beschrieben behandelt. Es gilt jedoch die Ausnahme, dass jeder Member (auf den das NonSerializedAttribute-Attribut nicht angewendet wurde) so behandelt wird, als ob das EnumMemberAttribute-Attribut angewendet wurde. Zum Beispiel verfügt die folgende Enumeration implizit über einen Datenvertrag, der dem Datenvertrag im vorherigen CarConditionEnum-Beispiel entspricht.

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

Sie können einfache Enumerationen verwenden, wenn es nicht erforderlich ist, den Datenvertragsnamen und den Namespace der Enumeration und die Enumerationsmemberwerte anzupassen.

Hinweise zu einfachen Enumerationen

Wenn Sie das EnumMemberAttribute-Attribut auf einfache Enumerationen anwenden, hat dies keine Auswirkung.

Es ist unerheblich, ob Sie das SerializableAttribute-Attribut auf die Enumeration anwenden.

Die Tatsache, dass die DataContractSerializer-Klasse das NonSerializedAttribute-Attribut berücksichtigt, das auf Enumerationsmember angewendet wird, gilt nicht für das Verhalten von BinaryFormatter und SoapFormatter. Beide Serialisierungsprogramme ignorieren das NonSerializedAttribute-Attribut.

Flagenumerationen

Sie können das FlagsAttribute-Attribut auf Enumerationen anwenden. In diesem Fall kann eine Liste von null oder mehr Enumerationswerten gleichzeitig gesendet oder empfangen werden.

Wenden Sie dazu das DataContractAttribute-Attribut auf die Flagenumeration an, und markieren Sie dann alle Member, die Potenzen von 2 sind, mit dem EnumMemberAttribute-Attribut. Beachten Sie, dass es sich bei der Progression um eine ununterbrochene Folge mit Potenzen des Werts 2 handeln muss (beispielsweise 1, 2, 4, 8, 16, 32, 64).

Die folgenden Schritte gelten für das Senden des Enumerationswerts eines Flags:

  1. Versuchen Sie, einen Enumerationsmember zu finden (mit angewendetem EnumMemberAttribute-Attribut), der dem numerischen Wert zugeordnet ist. Wenn Sie einen Member finden, senden Sie eine Liste, die nur diesen Member enthält.

  2. Versuchen Sie, den numerischen Wert so zu einer Summe aufzulösen, dass Enumerationsmember vorhanden sind (jeweils mit angewendetem EnumMemberAttribute-Attribut), die den einzelnen Teilen der Summe zugeordnet sind. Senden Sie die Liste mit allen Membern dieser Art. Beachten Sie, dass zum Auffinden einer solchen Summe der gierige Algorithmus verwendet wird. Daher ist nicht garantiert, dass eine Summe gefunden wird, auch wenn eine Summe vorhanden ist. Um dieses Problem zu verhindern, sollten Sie sicherstellen, dass es sich bei den numerischen Werten der Enumerationsmember um Potenzen von 2 handelt.

  3. Lösen Sie eine SerializationException aus, wenn die vorausgehenden zwei Schritte fehlschlagen und der numerische Wert ungleich 0 ist. Senden Sie die leere Liste, wenn der numerische Wert 0 ist.

Beispiel

Das folgende Enumerationsbeispiel kann für einen Flagvorgang verwendet werden.

[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

Die folgenden Beispielwerte werden dabei wie angegeben serialisiert.

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.

Siehe auch