資料合約中的列舉型別
列舉可以在資料合約模型中表示。 本主題將逐步介紹幾個範例,說明程式設計模型。
列舉基本知識
使用資料合約模型中列舉型別的其中一種方法是,將 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 用戶端,匯出的結構描述就會保存數值。 請注意,使用使用 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 屬性,與 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.