2012-02-15 20 views
5

Me encontré con el uso del operador más (+) en una definición enum hoy, me sorprendió ver pasar las pruebas correspondientes. ¿Alguien tiene alguna idea de dónde puede documentarse esto?El operador más en la definición enum

public enum ApprovalItemState 
{ 
    Enqueued = 1, 
    Approved = 2, 
    Denied = 4, 
    Acknowledged = 8, 
    ApprovalAcknowledged = ApprovalItemState.Approved + ApprovalItemState.Acknowledged, 
    DenialAcknowledged = ApprovalItemState.Denied + ApprovalItemState.Acknowledged 
} 


[TestClass] 
public class ApprovalItemStateTests 
{ 
    [TestMethod] 
    public void AreFlagsDeniedAndAcknowledged() 
    { 
     Assert.AreEqual(ApprovalItemState.DenialAcknowledged, ApprovalItemState.Denied | ApprovalItemState.Acknowledged); 
    } 

    [TestMethod] 
    public void IsDenialAcknowledged() 
    { 
     Assert.IsTrue(Enum.IsDefined(typeof(ApprovalItemState), ApprovalItemState.Denied | ApprovalItemState.Acknowledged)); 
     Assert.AreEqual(ApprovalItemState.Denied | ApprovalItemState.Acknowledged, (ApprovalItemState)Enum.Parse(typeof(ApprovalItemState), "DenialAcknowledged")); 
    } 


    [TestMethod] 
    public void IsNotDeniedAndApproved() 
    { 
     Assert.IsFalse(Enum.IsDefined(typeof(ApprovalItemState), ApprovalItemState.Approved | ApprovalItemState.Denied)); 
    } 
} 
+7

Se debe a que los valores de enumeración son (por defecto) los valores simplemente Int32. Sería mejor si el codificador utilizara | (bit O) en cambio, creo. –

+0

@DanielPratt Desarrollado un poco, que también podría publicarse como la respuesta. :) –

+0

Creo que deberías usar bitwise O en su lugar, si quieres utilizar esta enumeración como indicador, esto puede ser útil: http://msdn.microsoft.com/en-us/library/system.flagsattribute.aspx – 0lukasz0

Respuesta

10

El lenguaje C# Spec, en 14.5, establece lo siguiente:

Los siguientes operadores pueden utilizarse en los valores de los tipos de enumeración: ==,! =, <,>, < =,> = (§ 7.10.5), binario + (§7.8.4), binario - (§7.8.5), ^, &, | (§7.11.2), ~ (§7.7.4), ++ y - (§7.6.9 y §7.7.5).

Básicamente, ya que la enumeración se almacena internamente como un Int32 (que es el valor por defecto, a menos que se especifique un tipo de almacenamiento diferente), puede utilizar además como esta.

Sin embargo, es mucho más común el uso de | en lugar de + para definir máscaras. Además, sería común incluir [Flags] si va a utilizar esto como una enumeración de banderas.

+0

I piense que '[Flags]' no se está utilizando porque algunos estados técnicamente no serían lógicos. – Dave

+1

@Dave: Esa es una razón terrible para no usar el atributo Flags. –

+0

@MarkByers ¿Cuidar para elaborar? – Dave

2

De the C# reference on enum:

... Cada tipo de enumeración tiene un tipo subyacente, que puede ser cualquier tipo integral, excepto carbón. El valor predeterminado tipo subyacente de los elementos de enumeración es int ...

Por cierto, es más idiomático (y menos propenso a errores) para utilizar | en lugar de + para combinar valores de indicador de enumeración. Por ejemplo, este error no causará un problema:

DenialAcknowledged = 
    ApprovalItemState.Denied 
    | ApprovalItemState.Acknowledged 
    | ApprovalItemState.Denied 

Pero este error se causar un problema:

DenialAcknowledged = 
    ApprovalItemState.Denied 
    + ApprovalItemState.Acknowledged 
    + ApprovalItemState.Denied 
0

No es de extrañar - enumeraciones están representados por tipos integrales. También puede usar otros operadores, aunque si va a usar flags (que este ejemplo está haciendo), es mucho mejor usar el atributo [Flags] para definirlos y mejor para diseñar los bits con mayor claridad:

[Flags] 
public enum ApprovalItemState 
{ 
    Enqueued = 1 << 0, 
    Approved = 1 << 1, 
    Denied = 1 << 2, 
    Acknowledged = 1 << 3, 
    ApprovalAcknowledged = ApprovalItemState.Approved | ApprovalItemState.Acknowledged, 
    DenialAcknowledged = ApprovalItemState.Denied | ApprovalItemState.Acknowledged 
} 
1

Approved + Acknowledged es solo una constante por lo que se puede asignar como un valor al elemento enum. En cuanto a las pruebas de - que funcionan porque son valores int "felices", por lo (a + b) == (a | b)

Sin embargo, si el cambio que a algo así:

public enum ApprovalItemState 
{ 
    Enqueued = 1, 
    Approved = 2, 
    Denied = 7, 
    Acknowledged = 18, 
    ApprovalAcknowledged = Approved + Acknowledged, 
    DenialAcknowledged = Denied + Acknowledged 
} 

y pruebas no pasarán.

0

Voy a desglosar uno de estos para usted.

DenialAcknowledged = ApprovalItemState.Denied + ApprovalItemState.Acknowledged 
DenialAcknowledged = 4 + 8 
DenialAcknowledged = 12 

Para esta prueba:

[TestMethod] 
public void AreFlagsDeniedAndAcknowledged() 
{ 
    Assert.AreEqual(ApprovalItemState.DenialAcknowledged, ApprovalItemState.Denied | ApprovalItemState.Acknowledged); 
} 

Estás mirando:

ApprovalItemState.DenialAcknowledged == ApprovalItemState.Denied | ApprovalItemState.Acknowledged 
12 == 4 | 8 
12 == 0100 | 1000 //bitwise operation 
12 == 1100 
12 == 12 //convert from binary to decimal 

Y es por eso que el paso de la prueba. No es exactamente sencillo al mirar el código.

11

La respuesta de Reed es, por supuesto, correcta. Solo pensé en agregar un poco de trivia interesante. En primer lugar, cuando está dentro de la enumeración, todos los miembros de la enumeración están dentro del alcance. ¡Esta es la única situación en C# en la que puede usar un miembro enum a través de su nombre no calificado!

public enum ApprovalItemState 
{ 
    Enqueued = 1, 
    Approved = 2, 
    Denied = 4, 
    Acknowledged = 8, 
    ApprovalAcknowledged = Approved | Acknowledged, 
    DenialAcknowledged = Denied | Acknowledged 
} 

El segundo punto de preguntas y respuestas es que el compilador de C# permite realmente que la aritmética enumeración la participación de otras enumeraciones dentro de una enumeración!

enum E 
{ 
    E1 
} 
enum F 
{ 
    F1 
} 
enum G 
{ 
    G1 = E.E1 + F.F1 
} 

Normalmente, eso no sería del todo legal; no puede agregar dos enumeraciones diferentes juntas y no puede asignar el resultado. El compilador relaja las reglas dentro de un inicializador enumeración de modo que usted puede hacer cosas como:

enum MyFlags 
{ 
    MyReadOnly = FileFlags.ReadOnly, 
    ... 
Cuestiones relacionadas