Поделиться через


Enumeration Types in Data Contracts

Enumerations can be expressed in the data contract model. This topic walks through several examples that explain the programming model.

Enumeration Basics

One way to use enumeration types in the data contract model is to apply the DataContractAttribute attribute to the type. You must then apply the EnumMemberAttribute attribute to each member that must be included in the data contract.

The following example shows two classes. The first uses the enumeration and the second defines the enumeration.

<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 
[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
}

An instance of the Car class can be sent or received only if the condition field is set to one of the values New, Used, or Rental. If the condition is Broken or Stolen, a SerializationException is thrown.

You can use the DataContractAttribute properties (Name and Namespace) as usual for enumeration data contracts.

Enumeration Member Values

Generally the data contract includes enumeration member names, not numerical values. However, when using the data contract model, if the receiving side is a WCF client, the exported schema preserves the numerical values. Note that this is not the case when using the Using the XmlSerializer Class.

In the preceding example, if condition is set to Used and the data is serialized to XML, the resulting XML is <condition>Used</condition> and not <condition>1</condition>. Therefore, the following data contract is equivalent to the data contract of CarConditionEnum.

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

For example, you can use CarConditionEnum on the sending side and CarConditionWithNumbers on the receiving side. Although the sending side uses the value "1" for Used and the receiving side uses the value "20," the XML representation is <condition>Used</condition> for both sides.

To be included in the data contract, you must apply the EnumMemberAttribute attribute. In the .NET Framework, you can always apply the special value 0 (zero) to an enumeration, which is also the default value for any enumeration. However, even this special zero value cannot be serialized unless it is marked with the EnumMemberAttribute attribute.

There are two exceptions to this:

  • Flag enumerations (discussed later in this topic).

  • Enumeration data members with the EmitDefaultValue property set to false (in which case, the enumeration with the value zero is omitted from the serialized data).

Customizing Enumeration Member Values

You can customize the enumeration member value that forms a part of the data contract by using the Value property of the EnumMemberAttribute attribute.

For example, the following data contract is also equivalent to the data contract of the CarConditionEnum.

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

When serialized, the value of PreviouslyOwned has the XML representation <condition>Used</condition>.

Simple Enumerations

You can also serialize enumeration types to which the DataContractAttribute attribute has not been applied. Such enumeration types are treated exactly as previously described, except that every member (that does not have the NonSerializedAttribute attribute applied) is treated as if the EnumMemberAttribute attribute has been applied. For example, the following enumeration implicitly has a data contract equivalent to the preceding CarConditionEnum example.

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

You can use simple enumerations when you do not need to customize the enumeration's data contract name and namespace and the enumeration member values.

Notes on Simple Enumerations

Applying the EnumMemberAttribute attribute to simple enumerations has no effect.

It makes no difference whether or not the SerializableAttribute attribute is applied to the enumeration.

The fact that the DataContractSerializer class honors the NonSerializedAttribute attribute applied to enumeration members is different from the behavior of the BinaryFormatter and the SoapFormatter. Both of those serializers ignore the NonSerializedAttribute attribute.

Flag Enumerations

You can apply the FlagsAttribute attribute to enumerations. In that case, a list of zero or more enumeration values can be sent or received simultaneously.

To do so, apply the DataContractAttribute attribute to the flag enumeration and then mark all the members that are powers of two with the EnumMemberAttribute attribute. Note that to use a flag enumeration, the progression must be an uninterrupted sequence of powers of 2 (for example, 1, 2, 4, 8, 16, 32, 64).

The following steps apply to sending a flag's enumeration value:

  1. Attempt to find an enumeration member (with the EnumMemberAttribute attribute applied) that maps to the numeric value. If found, send a list that contains just that member.

  2. Attempt to break the numeric value into a sum such that there are enumeration members (each with the EnumMemberAttribute attribute applied) that map to each part of the sum. Send the list of all these members. Note that the greedy algorithm is used to find such a sum, and thus there is no guarantee that such a sum is found even if it is present. To avoid this problem, make sure that the numeric values of the enumeration members are powers of two.

  3. If the preceding two steps fail, and the numeric value is nonzero, throw a SerializationException. If the numeric value is zero, send the empty list.

Example

The following enumeration example can be used in a flag operation.

<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 
[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
}

The following example values are serialized as indicated.

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.
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

See Also

Reference

DataContractSerializer

Concepts

Using Data Contracts
Specifying Data Transfer in Service Contracts