列挙はデータ コントラクト モデルで表現できます。このトピックでは、いくつかの例を通してプログラミング モデルを説明します。
列挙の基本
データ コントラクト モデルで列挙型を使用する 1 つの方法として、DataContractAttribute 属性を型に割り当てる方法があります。この場合、データ コントラクトに含める必要のある各メンバに EnumMemberAttribute 属性を適用する必要があります。
2 つのクラスを次の例に示します。1 つ目は列挙を使用し、2 つ目は列挙を定義します。
<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 }
Car クラスのインスタンスは、condition フィールドの値が New、Used、Rental のいずれかに設定されている場合にのみ送受信できます。condition が Broken または Stolen の場合、SerializationException がスローされます。
DataContractAttribute プロパティ (Name と Namespace) は、列挙データ コントラクトとして通常通り使用できます。
列挙メンバ値
データ コントラクトには、数値ではなく列挙メンバ名が含まれます。
前の例では、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 属性を使用してマークされない限りシリアル化できません。
これには、次のような 2 つの例外があります。
-
フラグ列挙体 (このトピックの後で説明)
-
false (この場合、値ゼロを持つ列挙は単にシリアル化されたデータから除外される) に設定された EmitDefaultValue プロパティを持つ列挙データ メンバ
列挙メンバ値のカスタマイズ
データ コントラクトの一部を形成する列挙メンバ値は、EnumMemberAttribute 属性の Value プロパティを使ってカスタマイズできます。
たとえば、次のデータ コントラクトは 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 属性を列挙に適用してもしなくても変わりはありません。
列挙メンバに適用された NonSerializedAttribute 属性は DataContractSerializer に受け入れられるという事実は、BinaryFormatter と SoapFormatter の動作とは異なります。この 2 つのシリアライザは NonSerializedAttribute 属性を無視します。
フラグ列挙体
FlagsAttribute 属性を列挙体に適用できます。この場合、ゼロ個以上の列挙値のリストを同時に送受信できます。フラグの列挙値を送信するルールは次のとおりです。
-
数値にマップする列挙メンバ (EnumMemberAttribute 属性が適用されている) の検索を試みます。見つかった場合、そのメンバのみを含むリストを送信します。
-
合計の各部にマップされる列挙メンバ (それぞれに EnumMemberAttribute 属性が適用されている) が存在するような形で、数値をこの合計に分割します。このメンバすべてのリストを送信します。このような合計を見つけるには欲張りアルゴリズムが使用されます。したがって、このような合計が存在したとしても見つかるとは限りません。この問題を回避するには、列挙メンバの数値を必ず 2 の累乗数にします。
-
前の 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