2009-02-09 6 views
13

J'ai ce ENUM:C#: Enum.IsDefined sur les drapeaux combinés

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

Je suis en train de faire une enveloppe sur ce (ou tout, vraiment) ENUM qui notifie le changement. il ressemble actuellement à ceci:

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")); 
      } 
     } 
    } 
} 

Depuis un ENUM peut être attribué à une valeur vraiment, je veux vérifier si la valeur donnée est définie. Mais j'ai trouvé un problème. Si je lui donne ici une énumération constituée par exemple de Csv | Excel, alors Enum.IsDefined renverra false. Apparemment parce que je n'ai défini aucune énumération composée de ces deux-là. Je suppose que sur un certain niveau est logique, mais comment puis-je vérifier si la valeur donnée est valide? En d'autres termes, pour le faire fonctionner, de quoi ai-je besoin pour échanger cette ligne suivante?

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

Voir mon extension de Enum.IsDefined ci-dessous. –

Répondre

8

Avec les énumérations basées sur les indicateurs, il s'agit d'avoir un bit défini ou non. Donc, pour 'ExportFormat', si le bit 1 est défini, c'est le format CSV, même s'il y a plus de bits. Est-ce que les bits 1 et 2 définissent une valeur invalide? C'est subjectif: du point de vue des valeurs en tant que groupe, il est invalide (il n'y a pas de bit défini pour les bits 1 et 2) mais comme chaque valeur est un bit, en les regardant individuellement, cela peut être un La valeur avec les bits 1 et 2 set est valide.

Si l'on passe la valeur 0011111011, est-ce une valeur valide? Eh bien, cela dépend de ce que vous cherchez: si vous regardez la valeur entière, alors c'est une valeur invalide, mais si vous regardez des bits individuels, c'est une valeur correcte: il y a des bits qui ne sont pas défini, mais cela ne pose aucun problème, car les enums basés sur les drapeaux sont vérifiés 'par bit': vous ne les comparez pas à une valeur, vous vérifiez si un bit est défini ou non. Donc, comme votre logique vérifie quels bits sont sélectionnés pour sélectionner les formats à choisir, il n'est vraiment pas nécessaire de vérifier si la valeur enum est définie: vous avez 3 formats: si le bit du format correspondant est défini , le format est sélectionné. C'est la logique que vous devriez écrire.

+0

Et comment écririez-vous cette logique? – Svish

+0

La logique à laquelle je faisais référence est la logique qui teste si un bit est défini. Donc dans votre cas, vous avez 3 tests de bits, un pour chaque bit défini dans l'énumération, et agissez en conséquence, par ex. 'Csv' est défini, donc exporter en utilisant Csv. –

+0

ah ok, alors je pense que je l'ai eu :) merci pour le conseil! – Svish

0

Voir here. Assez de code.

+0

wow, je vais dire ... – Svish

5

Je fonctionne au niveau du bit et vérifiez si tous les bits dans la nouvelle valeur sont définies dans votre All valeur:

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

Vous devrez vous voir comment vous mieux faire cela, peut-être que vous avez besoin pour convertir toutes les valeurs en un int et ensuite faire la comparaison au niveau du bit.

+0

Cela pourrait fonctionner, sauf que je ne peux pas utiliser & nor == sur les opérandes de type T. Donc, je devrais commencer à convertir et à lancer, et je voudrais éviter cela ... – Svish

+0

Cela répond en fait à la question dans un manière raisonnable et utile. Il ne trouvera pas de combinaisons invalides, mais il peut éliminer les bits non valides et simples. – Wilbert

2

peut-être essayer de rattraper avec analyse?
Quelles sont les valeurs que vous ne voulez pas dépasser?

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

Je sais ce fil n'a pas été répondu dans un certain temps, mais j'ai pensé répondre à l'aide d'une fonction intégrée est bon pour ceux qui visitent ce après moi.

En utilisant l'énumération d'origine de l'OP, vous pouvez analyser une valeur bitmasked en utilisant le code suivant.

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

Espérons que ça aide.

9

Nous savons qu'une valeur enum convertie en chaîne ne commencera jamais par un chiffre, mais celle qui a une valeur invalide le sera toujours.Voici la solution la plus 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; 
} 

Utilisez cette méthode d'extension au lieu du stock IsDefined et qui devrait résoudre votre problème.

+4

Vous pouvez également vérifier le caractère '-', car les enums peuvent être représentés par des valeurs signées. – sethobrien

+0

Edité pour inclure l'excellente suggestion de sethobrien. –

+0

Une approche brillante! Merci beaucoup. ** PS: ** Le type de retour de votre méthode est erroné. Vous avez probablement pensé à retourner un booléen? ;) – KnorxThieus

0

Here est une petite méthode d'extension qui le fait efficacement.

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 
} 

L'approche suivante fonctionnera pour les éléments combinés par le code, qui n'est pas regroupés dans le ENUM:

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; 
} 

Et si vous êtes dans C# 4.0 où DLR est pris en charge, vous pouvez utiliser la méthode d'extension agnostique frais suivant:

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; 
} 

Remarque - Il doit être fait de cette façon depuis:

  1. Les opérateurs | et & ne peuvent pas être appliqués aux opérandes de type Enum et Enum
  2. Ces opérateurs sont définis dans le compilateur et ne sont pas reflétés, donc il n'y a aucun moyen de les récupérer avec la réflexion/Linq expressions, faites-moi confiance - I J'ai tout essayé ...
Questions connexes