Typy wyliczeniowe w kontraktach danych

Wyliczenia można wyrazić w modelu kontraktu danych. W tym temacie przedstawiono kilka przykładów wyjaśniających model programowania.

Podstawy wyliczenia

Jednym ze sposobów użycia typów wyliczenia w modelu kontraktu danych jest zastosowanie atrybutu DataContractAttribute do typu. Następnie należy zastosować EnumMemberAttribute atrybut do każdego elementu członkowskiego, który musi zostać uwzględniony w umowie danych.

W poniższym przykładzie przedstawiono dwie klasy. Pierwszy używa wyliczenia, a drugi definiuje wyliczenie.

[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

Wystąpienie Car klasy można wysłać lub odebrać tylko wtedy, gdy condition pole jest ustawione na jedną z wartości New, Usedlub Rental. Jeśli parametr condition ma Broken wartość lub Stolen, SerializationException jest zgłaszany.

Właściwości (Name i Namespace) można używać DataContractAttribute jak zwykle w przypadku kontraktów danych wyliczania.

Wyliczenie wartości składowych

Ogólnie rzecz biorąc, kontrakt danych zawiera nazwy elementów członkowskich wyliczenia, a nie wartości liczbowe. Jednak w przypadku korzystania z modelu kontraktu danych, jeśli strona odbierania jest klientem WCF, wyeksportowany schemat zachowuje wartości liczbowe. Należy pamiętać, że nie jest tak w przypadku używania klasy XmlSerializer.

Jeśli w poprzednim przykładzie condition ustawiono wartość Used , a dane są serializowane na XML, wynikowy kod XML to <condition>Used</condition> , a nie <condition>1</condition>. W związku z tym następujący kontrakt danych jest odpowiednikiem kontraktu danych .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

Na przykład można użyć po CarConditionEnum stronie wysyłania i CarConditionWithNumbers po stronie odbierania. Mimo że strona wysyłania używa wartości "1" dla Used , a strona odbierania używa wartości "20", reprezentacja XML jest <condition>Used</condition> dla obu stron.

Aby uwzględnić w umowie danych, należy zastosować EnumMemberAttribute atrybut . W programie .NET Framework zawsze można zastosować wartość specjalną 0 (zero) do wyliczenia, która jest również wartością domyślną dla dowolnego wyliczenia. Jednak nawet ta specjalna wartość zerowa nie może być serializowana, chyba że jest oznaczona atrybutem EnumMemberAttribute .

Istnieją dwa wyjątki:

  • Flagowanie wyliczenia (omówione w dalszej części tego tematu).

  • Wyliczenie elementów członkowskich danych z właściwością EmitDefaultValue ustawioną na false (w tym przypadku wyliczenie z wartością zero zostanie pominięte z danych serializowanych).

Dostosowywanie wartości składowych wyliczenia

Wartość elementu członkowskiego wyliczenia, która stanowi część kontraktu danych, można dostosować przy użyciu Value właściwości atrybutu EnumMemberAttribute .

Na przykład poniższy kontrakt danych jest również odpowiednikiem kontraktu danych .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

Po serializacji wartość PreviouslyOwned elementu ma reprezentację <condition>Used</condition>XML .

Proste wyliczenia

Można również serializować typy wyliczenia, do których DataContractAttribute atrybut nie został zastosowany. Takie typy wyliczenia są traktowane dokładnie tak, jak opisano wcześniej, z tą różnicą, że każdy element członkowski (który nie ma zastosowanego atrybutu NonSerializedAttribute ) jest traktowany tak, jakby EnumMemberAttribute atrybut został zastosowany. Na przykład poniższa wyliczenie niejawnie ma kontrakt danych odpowiadający poprzedniemu przykładowi CarConditionEnum .

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

Proste wyliczenia można użyć, gdy nie trzeba dostosowywać nazwy kontraktu danych i przestrzeni nazw wyliczenia oraz wartości elementów członkowskich wyliczenia.

Uwagi dotyczące prostych wyliczeń

Zastosowanie atrybutu EnumMemberAttribute do prostych wyliczeń nie ma efektu.

Nie ma znaczenia, czy SerializableAttribute atrybut jest stosowany do wyliczenia.

Fakt, że DataContractSerializer klasa honoruje NonSerializedAttribute atrybut zastosowany do składowych wyliczenia, różni się od zachowania BinaryFormatter elementu i SoapFormatter. Oba te serializatory ignorują NonSerializedAttribute atrybut .

Wyliczenia flag

Atrybut można zastosować FlagsAttribute do wyliczenia. W takim przypadku można wysłać lub odebrać jednocześnie listę wartości wyliczenia zero lub więcej.

W tym celu zastosuj DataContractAttribute atrybut do wyliczenia flagi, a następnie oznacz wszystkie elementy członkowskie, które są uprawnieniami dwóch z atrybutem EnumMemberAttribute . Należy pamiętać, że aby użyć wyliczenia flagi, progresja musi być nieprzerwaną sekwencją uprawnień 2 (na przykład 1, 2, 4, 8, 16, 32, 64).

Następujące kroki dotyczą wysyłania wartości wyliczenia flagi:

  1. Próba znalezienia elementu członkowskiego wyliczenia (z zastosowanym atrybutem EnumMemberAttribute ) mapowania na wartość liczbową. Jeśli zostanie znaleziona, wyślij listę zawierającą tylko ten element członkowski.

  2. Spróbuj podzielić wartość liczbową na sumę, tak aby istniały elementy członkowskie wyliczenia (każdy z zastosowanym atrybutem EnumMemberAttribute ), które są mapowane na każdą część sumy. Wyślij listę wszystkich tych członków. Należy pamiętać, że algorytm chciwości jest używany do znalezienia takiej sumy, a tym samym nie ma gwarancji, że taka suma zostanie znaleziona nawet wtedy, gdy jest obecna. Aby uniknąć tego problemu, upewnij się, że wartości liczbowe elementów członkowskich wyliczenia są uprawnieniami dwóch.

  3. Jeśli poprzednie dwa kroki kończą się niepowodzeniem, a wartość liczbowa jest niezerowa, należy zgłosić SerializationExceptionwartość . Jeśli wartość liczbowa to zero, wyślij pustą listę.

Przykład

Poniższy przykład wyliczenia może być używany w operacji flagi.

[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

Poniższe przykładowe wartości są serializowane zgodnie ze wskazaniem.

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.

Zobacz też