Tipos de enumeração em contratos de dados

As enumerações podem ser expressas no modelo de contrato de dados. Este tópico apresenta vários exemplos que explicam o modelo de programação.

Noções básicas de enumeração

Uma maneira de usar tipos de enumeração no modelo de contrato de dados é aplicar o atributo DataContractAttribute ao tipo. Em seguida, você deve aplicar o atributo EnumMemberAttribute a cada membro que deve ser incluído no contrato de dados.

O exemplo a seguir mostra duas classes. A primeira usa a enumeração e a segunda define a enumeração.

[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

Uma instância da classe Car só poderá ser enviada ou recebida se o campo condition estiver definido como um dos valores New, Used ou Rental. Se condition for Broken ou Stolen, uma SerializationException será lançada.

Você pode usar as propriedades DataContractAttribute (Name e Namespace) como de costume nos contratos de dados de enumeração.

Valores de membro de enumeração

Geralmente, o contrato de dados inclui nomes de membro de enumeração, não valores numéricos. No entanto, ao usar o modelo de contrato de dados, se o lado receptor for um cliente WCF, o esquema exportado preservará os valores numéricos. Observe que isso não acontece ao usar a classe XmlSerializer.

No exemplo anterior, se condition estiver definido como Used e os dados forem serializados como XML, o XML resultante será <condition>Used</condition> e não <condition>1</condition>. Portanto, o contrato de dados a seguir é equivalente ao contrato de dados de 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

Por exemplo, você pode usar CarConditionEnum no lado de envio e CarConditionWithNumbers no lado receptor. Embora o lado de envio use o valor "1" para Used e o lado receptor use o valor "20", a representação XML é <condition>Used</condition> para ambos os lados.

Para ser incluído no contrato de dados, você deve aplicar o atributo EnumMemberAttribute. No .NET Framework, você sempre pode aplicar o valor especial 0 (zero) a uma enumeração, que também é o valor padrão de qualquer enumeração. No entanto, mesmo esse valor zero especial não pode ser serializado, a menos que seja marcado com o atributo EnumMemberAttribute.

Há duas exceções:

  • Enumerações de sinalizador (discutidas posteriormente, neste tópico).

  • Os membros de dados de enumeração com a propriedade EmitDefaultValue definida como false (nesse caso, a enumeração com o valor zero é omitida dos dados serializados).

Personalizando valores de membro de enumeração

Você pode personalizar o valor do membro de enumeração que faz parte do contrato de dados usando a propriedade Value do atributo EnumMemberAttribute.

Por exemplo, o contrato de dados a seguir é também equivalente ao contrato de dados de 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

Quando serializado, o valor de PreviouslyOwned tem a representação XML <condition>Used</condition>.

Enumerações simples

Você também pode serializar tipos de enumeração aos quais o atributo DataContractAttribute não foi aplicado. Esses tipos de enumeração são tratados exatamente como descrito anteriormente, exceto que cada membro (que não tem o atributo NonSerializedAttribute aplicado) é tratado como se o atributo EnumMemberAttribute tivesse sido aplicado. Por exemplo, a enumeração a seguir tem implicitamente um contrato de dados equivalente ao exemplo anterior de CarConditionEnum.

public enum CarCondition
{
    New,
    Used,
    Rental,
    [NonSerialized]
    Lost
}
Public Enum CarCondition
    [New]
    Used
    Rental
End Enum

Você pode usar enumerações simples quando não precisar personalizar o nome e o namespace do contrato de dados da enumeração e os valores de membro da enumeração.

Observações sobre enumerações simples

A aplicação do atributo EnumMemberAttribute a enumerações simples não tem efeito.

Não faz diferença se o atributo SerializableAttribute é aplicado ou não à enumeração.

O fato de a classe DataContractSerializer honrar o atributo NonSerializedAttribute aplicado aos membros da enumeração é diferente do comportamento de BinaryFormatter e SoapFormatter. Esses dois serializadores ignoram o atributo NonSerializedAttribute.

Enumerações de sinalizador

Você pode aplicar o atributo FlagsAttribute a enumerações. Nesse caso, uma lista de zero ou mais valores de enumeração pode ser enviada ou recebida simultaneamente.

Para isso, aplique o atributo DataContractAttribute à enumeração de sinalizador e marque todos os membros que são potências de dois com o atributo EnumMemberAttribute. Observe que, para usar uma enumeração de sinalizador, a progressão deve ser uma sequência ininterrupta de potências de 2 (por exemplo, 1, 2, 4, 8, 16, 32, 64).

As seguintes etapas se aplicam ao envio do valor de enumeração de um sinalizador:

  1. Tente localizar um membro de enumeração (com o atributo EnumMemberAttribute aplicado) que mapeie até o valor numérico. Se encontrado, envie uma lista que contenha apenas esse membro.

  2. Tente dividir o valor numérico em uma soma de modo que haja membros de enumeração (cada um com o atributo EnumMemberAttribute aplicado) que mapeiem até cada parte da soma. Envie a lista de todos esses membros. Observe que o algoritmo greedy é usado para encontrar tal soma e, portanto, não há garantia de que essa soma seja encontrada, mesmo que ela esteja presente. Para evitar esse problema, verifique se os valores numéricos dos membros da enumeração são potências de dois.

  3. Se as duas etapas anteriores falharem e o valor numérico não for zero, gere uma SerializationException. Se o valor numérico for zero, envie a lista vazia.

Exemplo

O exemplo de enumeração a seguir pode ser usado em uma operação de sinalizador.

[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

Os valores de exemplo a seguir são serializados conforme indicado.

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.

Confira também