数据协定中的枚举类型

枚举可以在数据协定模型中表示。 本主题介绍介绍编程模型的几个示例。

枚举基础知识

在数据协定模型中使用枚举类型的一种方法是将 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字段设置为conditionNewUsed中的一个值时,才能发送或接收Rental类的实例。 如果 conditionBrokenStolen,则会引发一个 SerializationException

可以像往常一样将DataContractAttribute 属性(NameNamespace)用于枚举数据契约。

枚举成员值

通常,数据协定包括枚举成员名称,而不是数值。 但是,使用数据协定模型时,如果接收方是 WCF 客户端,则导出的架构将保留数值。 请注意,使用 Using the 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 它。

这有两个例外:

  • 标志枚举(本主题的后面部分将进行讨论)。

  • 带有EmitDefaultValue属性设置为false的枚举数据成员(在这种情况下,从序列化数据中省略值为零的枚举)。

自定义枚举成员值

可以通过使用 Value 属性 (attribute) 的 EnumMemberAttribute 属性 (property) 对作为数据协定构成部分的枚举成员值进行自定义。

例如,以下数据协定也等效于该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 属性的事实不同于 BinaryFormatterSoapFormatter 的行为。 这两个序列化程序都忽略了该 NonSerializedAttribute 属性。

标志枚举

可以将属性 FlagsAttribute 应用于枚举。 在这种情况下,可以同时发送或接收零个或多个枚举值的列表。

为此,请将DataContractAttribute 属性应用于标志枚举,并使用EnumMemberAttribute 属性标记所有是二的幂的成员。 请注意,若要使用标志枚举,进度必须是 2 的不间断权力序列(例如 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.

另请参阅