Язык

Типы перечисления в контрактах данных

Перечисления можно выразить в модели контракта данных. В этом разделе рассматриваются несколько примеров, объясняющих модель программирования.

Основы перечисления

Одним из способов использования типов перечислений в модели контракта данных является применение атрибута DataContractAttribute к типу. Затем необходимо применить атрибут EnumMemberAttribute к каждому участнику, который должен быть включен в договор о данных.

В следующем примере показаны два класса. Первый использует перечисление, а второй определяет перечисление.

[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

Экземпляр Car класса можно отправлять или получать только в том случае, если condition поле имеет одно из значений Newили UsedRental. Если condition равен Broken или Stolen, выбрасывается SerializationException.

Вы можете использовать свойства DataContractAttribute (Name и Namespace) как обычно для контрактов данных перечислений.

Значения элементов перечисления

Обычно контракт данных включает имена элементов перечисления, а не числовые значения. Однако при использовании модели контракта данных, если получатель является клиентом WCF, экспортируемая схема сохраняет числовые значения. Обратите внимание, что это не так, когда используется класс XmlSerializer.

В предыдущем примере, если condition установлено как Used, и данные сериализуются в XML, результирующий XML-код будет <condition>Used</condition>, а не <condition>1</condition>. Поэтому следующий контракт данных эквивалентен контракту данных 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

Например, можно использовать CarConditionEnum на стороне отправки и CarConditionWithNumbers на принимающей стороне. Хотя сторона отправки использует значение "1" для Used, а сторона получения использует значение "20", XML-представление стоит как <condition>Used</condition> для обеих сторон.

Чтобы включить его в контракт данных, необходимо применить EnumMemberAttribute атрибут. В .NET Framework всегда можно применить специальное значение 0 (ноль) к перечислению, которое также является значением по умолчанию для любого перечисления. Однако даже это специальное нулевое значение нельзя сериализовать, если он не помечен атрибутом EnumMemberAttribute .

Существует два исключения:

  • Перечисления флагов (описаны далее в этом разделе).

  • Элементы данных перечисления, у которых для свойств установлены EmitDefaultValuefalse, в этом случае перечисления с нулевым значением исключаются из сериализованных данных.

Настройка значений элементов перечисления

Можно настроить значение элемента перечисления, которое формирует часть контракта данных, с помощью свойства Value атрибута EnumMemberAttribute.

Например, следующий контракт данных также эквивалентен контракту данных объекта 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

При сериализации значение PreviouslyOwned имеет XML-представление <condition>Used</condition>.

Простые перечисления

Можно также сериализовать типы перечислений, к которым DataContractAttribute атрибут не был применен. Такие типы перечислений обрабатываются точно так же, как описано ранее, за исключением того, что каждый член, которому не применен атрибут NonSerializedAttribute, обрабатывается как если бы атрибут EnumMemberAttribute был применен. Например, следующее перечисление неявно имеет контракт данных, эквивалентный предыдущему CarConditionEnum примеру.

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

Можно использовать простые перечисления, если не нужно настраивать ни имя контракта данных перечисления, ни пространство имен, ни значения его элементов.

Заметки о простых перечислениях

Применение атрибута EnumMemberAttribute к простым перечислениям не оказывает никакого эффекта.

Это не имеет значения, применяется ли SerializableAttribute атрибут к перечислению.

Тот факт, что DataContractSerializer класс учитывает атрибут, NonSerializedAttribute применяемый к элементам перечисления, отличается от поведения BinaryFormatter и .SoapFormatter Оба этих сериализатора игнорируют NonSerializedAttribute атрибут.

Перечисления флагов

Атрибут FlagsAttribute можно применить к перечислениям. В этом случае может быть отправлен или получен список из нуля или более значений перечисления одновременно.

Для этого примените атрибут DataContractAttribute к перечислению флагов, а затем пометьте EnumMemberAttribute всех членов, которые являются степенями двух. Обратите внимание, что для использования перечисления флагов прогрессия должна быть непрерывной последовательностью степеней двойки (например, 1, 2, 4, 8, 16, 32, 64).

Следующие действия применяются к отправке значения перечисления флага:

  1. Попытайтесь найти элемент перечисления (с примененным атрибутом EnumMemberAttribute), который сопоставляется с числовым значением. При обнаружении отправьте список, содержащий только этот элемент.

  2. Попытайтесь разбить числовое значение на сумму так, чтобы элементы перечисления (каждый с применённым атрибутом EnumMemberAttribute) соответствовали каждой части суммы. Отправьте список всех этих членов. Обратите внимание, что жадный алгоритм используется для поиска такой суммы, и поэтому нет никакой гарантии, что такая сумма найдена даже в том случае, если она присутствует. Чтобы избежать этой проблемы, убедитесь, что числовые значения элементов перечисления являются степенями двойки.

  3. Если предыдущие два шага терпят неудачу, а числовое значение ненулевое, генерируйте исключение SerializationException. Если числовое значение равно нулю, отправьте пустой список.

Пример

Следующий пример перечисления можно использовать в флаговой операции.

[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

Приведенные ниже примеры значений сериализуются, как указано.

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.

См. также