数据协定中的枚举类型
枚举可以用数据协定模型来表示。本主题演练几个介绍编程模型的示例。
枚举基础知识
若要使用以数据协定模型表示的枚举类型,一种方法就是将 DataContractAttribute 属性应用于该类型。然后,必须将 EnumMemberAttribute 属性应用于每个必须在数据协定中包含的成员。
下面的示例演示了两个类。第一个类使用枚举,第二个类定义枚举。
<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
}
仅当将 condition
字段的值设置为 New
、Used
或 Rental
之一时,才可发送或接收 Car
类的实例。如果 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> 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,
}
例如,您可以在发送端使用 CarConditionEnum
并且在接收端使用 CarConditionWithNumbers
。尽管发送端的 Used
使用值“1”而接收端使用值“20”,但是两端的 XML 表示形式均为 <condition>Used</condition>
。
若要包含在数据协定中,必须应用 EnumMemberAttribute 属性。在 .NET Framework 中,可以始终将特殊值 0(零)应用到枚举中,它是任何枚举的默认值。但是,即使对于此特殊值零,也只能使用 EnumMemberAttribute 属性标记才能进行序列化。
以下为此规则的两种例外情况:
标志枚举(本主题的后面部分将进行讨论)。
EmitDefaultValue 属性设置为 false 的枚举数据成员(这种情况下将从序列化数据中省略值为零的枚举)。
自定义枚举成员值
可以通过使用 EnumMemberAttribute 属性 (attribute) 的 Value 属性 (property) 对作为数据协定构成部分的枚举成员值进行自定义。
例如,下面的数据协定也等效于 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
}
在序列化时,PreviouslyOwned
的值的 XML 表示形式为 <condition>Used</condition>
。
简单枚举
您还可以对未向其应用 DataContractAttribute 属性的枚举类型进行序列化。可以按照上述方式来处理这种枚举类型,只不过将每个成员(这些成员未应用 NonSerializedAttribute 属性)视为已应用了 EnumMemberAttribute 属性。例如,下面的枚举隐式具有一个与上面的 CarConditionEnum
示例等效的数据协定。
Public Enum CarCondition
[New]
Used
Rental
End Enum
public enum CarCondition
{
New,
Used,
Rental,
[NonSerialized]
Lost
}
如果您不需要自定义枚举的数据协定名称、命名空间以及枚举成员值,则可以使用简单枚举。
简单枚举说明
将 EnumMemberAttribute 属性应用于简单枚举是无效的。
是否将 SerializableAttribute 属性应用于简单枚举并没有任何区别。
DataContractSerializer 类采用应用于枚举成员的 NonSerializedAttribute 属性的事实不同于 BinaryFormatter 和 SoapFormatter 的行为。这两种序列化程序都忽略 NonSerializedAttribute 属性。
标志枚举
可以将 FlagsAttribute 属性应用于枚举。在这种情况下,可以同时发送或接收包含零个或多个枚举值的列表。
为此,请将 DataContractAttribute 属性应用于标志枚举,然后使用 EnumMemberAttribute 属性对所有为 2 的幂的成员进行标记。请注意,若要使用标志枚举,级数必须为不间断的 2 的幂的序列(例如,1、2、4、8、16、32、64)。
可以使用下面的步骤来发送标志的枚举值:
尝试查找映射到数值的枚举成员(应用了 EnumMemberAttribute 属性)。如果可以找到,请发送仅包含该成员的列表。
尝试将此数值分解为和的形式,以便枚举成员(每个成员都应用了 EnumMemberAttribute 属性)可以映射到和的各个部分。发送包含所有这些成员的列表。请注意,将使用“贪婪算法”**查找这样的和,因此即使存在这样的和,也不一定能找到它。为避免出现这种问题,请确保枚举成员的数值为 2 的幂。
如果上面的两个步骤均无法实现并且数值为非零,则引发一个 SerializationException。如果数值为零,则发送空列表。
示例
下面的枚举示例可用于标志操作。
<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
}
下面的示例值将按照指示的方式进行序列化。
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