2009-02-09 22 views
13

tengo esta enumeración:C#: Enum.IsDefined en las banderas combinadas

[Flags] 
public enum ExportFormat 
{ 
    None = 0, 
    Csv = 1, 
    Tsv = 2, 
    Excel = 4, 
    All = Excel | Csv | Tsv 
} 

que estoy tratando de hacer un envoltorio en este (o cualquier, en realidad) de enumeración que notifica el cambio. Actualmente se parece a esto:

public class NotifyingEnum<T> : INotifyPropertyChanged 
    where T : struct 
{ 
    private T value; 

    public event PropertyChangedEventHandler PropertyChanged; 

    public NotifyingEnum() 
    { 
     if (!typeof (T).IsEnum) 
      throw new ArgumentException("Type T must be an Enum"); 
    } 

    public T Value 
    { 
     get { return value; } 
     set 
     { 
      if (!Enum.IsDefined(typeof (T), value)) 
       throw new ArgumentOutOfRangeException("value", value, "Value not defined in enum, " + typeof (T).Name); 

      if (!this.value.Equals(value)) 
      { 
       this.value = value; 

       PropertyChangedEventHandler handler = PropertyChanged; 
       if (handler != null) 
        handler(this, new PropertyChangedEventArgs("Value")); 
      } 
     } 
    } 
} 

Desde una enumeración puede ser asignado con un valor de verdad, quiero comprobar si se ha definido el valor dado. Pero encontré un problema. Si aquí le doy una enumeración que consiste en, por ejemplo, Csv | Excel, entonces Enum.IsDefined devolverá false. Aparentemente porque no he definido ninguna enumeración que consta de esos dos. Supongo que en algún nivel es lógico, pero ¿cómo debo verificar si el valor dado es válido? En otras palabras, para que funcione, ¿con qué necesito cambiar esta línea?

if (!Enum.IsDefined(typeof (T), value)) 
+0

Vea mi extensión de Enum.IsDefined a continuación. –

Respuesta

8

Con enumeraciones basadas en banderas, se trata de tener un bit configurado o no. Entonces, para 'ExportFormato', si el bit 1 está configurado, es formato CSV, aunque haya más bits configurados. ¿Tiene el bit 1 y 2 establecido un valor no válido? Esto es subjetivo: desde el punto de vista de los valores como un grupo, no es válido (no hay un patrón de bits definido para los bits 1 y 2), sin embargo, dado que cada valor es un poco, mirándolos individualmente, puede ser que un el valor con los bits 1 y 2 es válido.

Si se pasa el valor 0011111011, ¿es ese un valor válido? Bueno, depende de lo que estés buscando: si estás mirando el valor total, entonces es un valor no válido, pero si estás buscando bits individuales, es un valor aceptable: tiene bits establecidos que no son definido, pero está bien, ya que las enumeraciones basadas en banderas se marcan 'por bit': no ​​las está comparando con un valor, está comprobando si un bit está establecido o no.

Así que, como su lógica comprobará qué bits están configurados para seleccionar qué formatos elegir, en realidad no es necesario verificar si el valor enum está definido: tiene 3 formatos: si el bit del formato correspondiente está configurado , el formato está seleccionado. Esa es la lógica que deberías escribir.

+0

¿Y cómo escribirías esa lógica? – Svish

+0

La lógica a la que me refería es la lógica que prueba si se establece un bit. Entonces, en su caso, tiene pruebas de 3 bits, una para cada bit definido en la enumeración, y actúa de acuerdo, p. Ej. 'Csv' está configurado, entonces exporte usando Csv. –

+0

ah ok, entonces creo que lo tengo :) ¡gracias por el consejo! – Svish

0

Ver here. Mucho código

+0

wow, diré ... – Svish

5

Me operar en el nivel de bits y comprobar si todos los bits puestos en el nuevo valor se establecen en su All valor:

if (! (All & NewValue) == NewValue) 

Tendrá que verse a sí mismo cuál es la mejor haces, tal vez usted necesita para convertir todos los valores a un int y luego hacer la comparación bit a bit.

+0

Eso podría funcionar, excepto que no puedo usar & nor == en operandos de tipo T. Tendría que comenzar a convertir y transmitir y cosas así, y me gustaría evitar que ... – Svish

+0

Esto realmente responde la pregunta en una forma razonable y útil. No encontrará combinaciones no válidas, pero puede eliminar los bits de conjunto no válidos de manera simple. – Wilbert

2

quizás intente catch with parse?
cuyos valores no quieres aprobar?

public T Value 
    { 
     get { return value; } 
     set 
     { 
      try 
      { 
       Enum.Parse(typeof(T), value.ToString()); 
      } 
      catch 
      { 
       throw new ArgumentOutOfRangeException("value", value, "Value not defined in enum, " + typeof(T).Name); 
      } 
      if (!this.value.Equals(value)) 
      { 
       this.value = value; 

       PropertyChangedEventHandler handler = PropertyChanged; 
       if (handler != null) 
        handler(this, new PropertyChangedEventArgs("Value")); 
      } 
     } 
    } 
1

Sé que este hilo no ha sido contestada en bastante tiempo, pero pensé que responder usando una función incorporada que es bueno para los que visitan esta detrás de mí.

Usando la enumeración original de OP, puede analizar un valor de máscara de bits usando el siguiente código.

ExportFormat format; 
    if (!Enum.TryParse<ExportFormat>(value.ToString(), out format)) 
    { 
     // Could not parse 
    } 

Espero que ayude.

9

Sabemos que un valor enum convertido a una cadena nunca comenzará con un dígito, pero siempre tendrá un valor inválido.Aquí está la solución más simple:

public static bool IsDefinedEx(this Enum yourEnum) 
{ 
    char firstDigit = yourEnum.ToString()[0]; 
    if (Char.IsDigit(firstDigit) || firstDigit == '-') // Account for signed enums too.. 
     return false; 

    return true; 
} 

usar que el método de extensión en lugar de la acción IsDefined y que debería resolver su problema.

+4

Es posible que también desee comprobar el carácter '-', ya que las enumeraciones pueden representarse mediante valores firmados. – sethobrien

+0

Editado para incluir la excelente sugerencia de Sethobrien. –

+0

Brillante enfoque! Muchas gracias. ** PD: ** El tipo de devolución de su método es incorrecto. ¿Probablemente pensaste en devolver un booleano? ;) – KnorxThieus

0

Here es un pequeño método de extensión que lo hace de manera eficiente.

static void Main(string[] args) 
{ 
    var x = ExportFormat.Csv | ExportFormat.Excel; 
    var y = ExportFormat.Csv | ExportFormat.Word; 
    var z = (ExportFormat)16; //undefined value 

    var xx = x.IsDefined(); //true 
    var yy = y.IsDefined(); //false 
    var zz = z.IsDefined(); //false 
} 

public static bool IsDefined(this Enum value) 
{ 
    if (value == null) return false; 
    foreach (Enum item in Enum.GetValues(value.GetType())) 
    if (item.HasFlag(value)) return true; 
    return false; 
} 

[Flags] 
public enum ExportFormat          
{ 
    None = 0, 
    Csv = 1, 
    Tsv = 2, 
    Excel = 4, 
    Word = 8, 
    All = Excel | Csv | Tsv 
} 

El enfoque siguiente trabajo para los artículos combinados por el código, que no se agrupa en la enumeración:

static void Main(string[] args) 
{ 
    var x = ExportFormat.Csv | ExportFormat.Excel; 
    var y = ExportFormat.Csv | ExportFormat.Word; 
    var z = (ExportFormat)16; //undefined value 

    var xx = x.IsDefined(); //true 
    var yy = y.IsDefined(); //true 
    var zz = z.IsDefined(); //false 
} 

public static bool IsDefined(this ExportFormat value) 
{ 
    var max = Enum.GetValues(typeof(ExportFormat)).Cast<ExportFormat>() 
    .Aggregate((e1,e2) => e1 | e2); 
    return (max & value) == value; 
} 

Y en caso de que esté en C# 4.0, donde se apoya DLR, puede utilizar el siguiente método de extensión agnóstico cool:

public static bool IsDefined(this Enum value) 
{ 
    dynamic dyn = value; 
    var max = Enum.GetValues(value.GetType()).Cast<dynamic>(). 
    Aggregate((e1,e2) => e1 | e2); 
    return (max & dyn) == dyn; 
} 

Nota - debe ser hecho de esta manera desde:

  1. Operadores | y & no se puede aplicar a operandos de tipo Enum y Enum
  2. Estos operadores se definen en el compilador y no se reflejan, así que no hay forma de recuperarlos con la reflexión/LINQ Expresiones, confía en mí - Lo he intentado todo ...
Cuestiones relacionadas