数据协定中的枚举类型

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

枚举基础知识

若要使用以数据协定模型表示的枚举类型,一种方法就是将 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 客户端,则导出的架构保留数值。 请注意,如果使用 使用 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。 尽管发送端的 Used 使用值“1”而接收端使用值“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 的幂的成员进行标记。 请注意,若要使用标志枚举,级数必须为不间断的 2 的幂的序列(例如,1、2、4、8、16、32、64)。

可以使用下面的步骤来发送标志的枚举值:

  1. 尝试查找映射到数值的枚举成员(应用了 EnumMemberAttribute 属性)。 如果可以找到,请发送仅包含该成员的列表。

  2. 尝试将此数值分解为和的形式,以便枚举成员(每个成员都应用了 EnumMemberAttribute 属性)可以映射到和的各个部分。 发送包含所有这些成员的列表。 请注意,将使用“贪婪算法”查找这样的和,因此即使存在这样的和,也不一定能找到它。 为避免出现这种问题,请确保枚举成员的数值为 2 的幂。

  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.

请参阅