枚举可以在数据协定模型中表示。 本主题介绍介绍编程模型的几个示例。
枚举基础知识
在数据协定模型中使用枚举类型的一种方法是将 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
或Used
中的一个值时,才能发送或接收Rental
类的实例。 如果 condition
为 Broken
或 Stolen
,则会引发一个 SerializationException。
可以像往常一样将DataContractAttribute 属性(Name 和 Namespace)用于枚举数据契约。
枚举成员值
通常,数据协定包括枚举成员名称,而不是数值。 但是,使用数据协定模型时,如果接收方是 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 属性的事实不同于 BinaryFormatter 和 SoapFormatter 的行为。 这两个序列化程序都忽略了该 NonSerializedAttribute 属性。
标志枚举
可以将属性 FlagsAttribute 应用于枚举。 在这种情况下,可以同时发送或接收零个或多个枚举值的列表。
为此,请将DataContractAttribute 属性应用于标志枚举,并使用EnumMemberAttribute 属性标记所有是二的幂的成员。 请注意,若要使用标志枚举,进度必须是 2 的不间断权力序列(例如 1、2、4、8、16、32、64)。
可以使用下面的步骤来发送标志的枚举值:
尝试找出应用了 EnumMemberAttribute 属性的枚举成员,以映射到数值。 如果找到,请发送仅包含该成员的列表。
尝试将数值分解成一个和,使得每个部分都有对应的枚举成员(为每个部分应用EnumMemberAttribute属性)。 发送所有这些成员的列表。 请注意,贪婪算法 用于查找此类总和,因此即使确实存在,也不能保证找到此类总和。 若要避免此问题,请确保枚举成员的数值是两个幂。
如果上面的两个步骤均无法实现并且数值为非零,则引发一个 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.