データ コントラクトの列挙型

列挙はデータ コントラクト モデルで表現できます。このトピックでは、いくつかの例を通してプログラミング モデルを説明します。

列挙の基本

データ コントラクト モデルで列挙型を使用する 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 フィールドの値が NewUsedRental のいずれかに設定されている場合にのみ送受信できます。condition が、Broken または Stolen の場合、SerializationException がスローされます。

DataContractAttribute プロパティ (NameNamespace) を、列挙データ コントラクトとして通常通り使用できます。

列挙メンバー値

通常、データ コントラクトには、数値ではなく列挙メンバー名が含まれます。ただし、データ コントラクト モデルを使用しているときに、受信側が WCF クライアントである場合、エクスポートされたスキーマは数値を保持します。これは、XmlSerializer クラスの使用を使用している場合は、当てはまりません。

前の例では、conditionUsed に設定され、データが 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 つの例外があります。

  • フラグ列挙体 (このトピックの後で説明)

  • EmitDefaultValue プロパティが false に設定された列挙データ メンバー (この場合、値がゼロの列挙はシリアル化されたデータから除外される)

列挙メンバー値のカスタマイズ

データ コントラクトの一部を形成する列挙メンバー値は、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 属性を列挙に適用してもしなくても変わりはありません。

DataContractSerializer クラスが、列挙メンバーに適用された NonSerializedAttribute 属性を受け入れるという事実は、BinaryFormatterSoapFormatter の動作とは異なります。この 2 つのシリアライザーは NonSerializedAttribute 属性を無視します。

フラグ列挙体

列挙体に FlagsAttribute 属性を適用できます。この場合、ゼロ個以上の列挙値のリストを同時に送受信できます。

これを行うには、DataContractAttribute 属性をフラグ列挙体に適用し、2 の累乗であるすべてのメンバーを、EnumMemberAttribute 属性を使用してマークします。フラグ列挙体を使用するには、数列は (1、2、4、8、16、32、64 のように) 2 の累乗の連続である必要があります。

フラグの列挙値を送信する手順を次に示します。

  1. 数値にマップする列挙メンバー (EnumMemberAttribute 属性が適用されている) の検索を試みます。見つかった場合、そのメンバーのみを含むリストを送信します。

  2. 合計の各部にマップされる列挙メンバー (それぞれに EnumMemberAttribute 属性が適用されている) が存在するような形で、数値をこの合計に分割します。このメンバーすべてのリストを送信します。このような合計を見つけるために、最長一致アルゴリズムが使用されます。したがって、このような合計が存在したとしても見つかるとは限りません。この問題を回避するには、列挙メンバーの数値を必ず 2 の累乗数にします。

  3. 前の 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

参照

リファレンス

DataContractSerializer

概念

データ コントラクトの使用
サービス コントラクトでのデータ転送の指定