2012-04-11 8 views
11

Ich versuche, eine Hilfsmethode für die Auflistung der Namen aller Bits in einem Enum-Wert (für Protokollierungszwecke) zu machen. Ich möchte eine Methode haben, die die Liste aller Enum-Werte in einigen Variablen zurückgeben würde. In meinem BeispielListen Sie alle Bit-Namen von einem Flag Enum

[Flag] 
Enum HWResponse 
{ 
    None = 0x0, 
    Ready = 0x1, 
    Working = 0x2, 
    Error = 0x80, 
} 

Ich füttere es 0x81, und es sollte mich sorgen mit einem IEnumerable<HWResponse>{Ready, Error} enthält.

Da ich keinen einfacheren Weg gefunden habe, habe ich versucht, den Code unten zu schreiben, aber ich kann es nicht kompilieren lassen.

public static IEnumerable<T> MaskToList<T>(Enum mask) 
{ 
    if (typeof(T).IsSubclassOf(typeof(Enum)) == false) 
    throw new ArgumentException(); 

    List<T> toreturn = new List<T>(100); 

    foreach(T curValueBit in Enum.GetValues(typeof (T)).Cast<T>()) 
    { 
    Enum bit = ((Enum) curValueBit); // Here is the error 

    if (mask.HasFlag(bit)) 
     toreturn.Add(curValueBit); 
    } 

    return toreturn; 
} 

Auf dieser Version des Codes, beschwert sich der Compiler, dass es nicht T Enum werfen können.

Was habe ich falsch gemacht? Gibt es einen besseren (einfacheren) Weg, dies zu tun? Wie könnte ich die Besetzung machen?

Außerdem habe ich versucht, das Verfahren, wie

public static IEnumerable<T> MaskToList<T>(Enum mask) where T:Enum 

aber ENUM ist von einer besonderen Art zu schreiben, die die ‚wo‘ Syntax (C# 4.0)

+1

Dies scheint nicht wie eine Flag Enum sein; Die Kombinationen ergeben keinen Sinn. Kann etwas gleichzeitig "arbeiten" und "fertig" sein? –

+0

@DBM: Das ist wahr, es ist nur ein dummes Beispiel – PPC

+0

@All: Vielen Dank für Ihre tollen Antworten. Alle 3 sind nützlich! – PPC

Antwort

19

Hier ist eine einfache Möglichkeit, es zu schreiben LINQ mit:

public static IEnumerable<T> MaskToList<T>(Enum mask) 
{ 
    if (typeof(T).IsSubclassOf(typeof(Enum)) == false) 
     throw new ArgumentException(); 

    return Enum.GetValues(typeof(T)) 
         .Cast<Enum>() 
         .Where(m => mask.HasFlag(m)) 
         .Cast<T>(); 
} 
+2

Große Lösung: einfach, lesbar und kurz. Ein Nachteil: HasFlag (m) listet auch den Status 'None' auf (0x0); aber es ist leicht zu überwinden durch einen alten Stil binary & – PPC

+1

Auch HasFlag() scheint große Leistungsprobleme zu haben: siehe einen anderen Thread auf http://StackOverflow.com/a/7164314/1121983 – PPC

+0

Funktioniert das tatsächlich? Wenn ich versuche, 'var Wert = (T) Enum.GetValues ​​(typeof (T)) zu werfen. Cast () .FirstOrDefault (m => mask.HasFlag (m)) Compiler beschwert sich. – JobaDiniz

3

Wenn die gewünschte Endergebnis ein verbieten ist String-Liste von Namen, rufen Sie einfach mask.ToString().

Was würden Sie tun, wenn die Enumeration wie folgt definiert wurden:

[Flags] 
enum State 
{ 
    Ready = 1, 
    Waiting = 2, 
    ReadyAndWaiting = 3 
} 

Wie der Compiler-Fehler zu lösen, dies sollte es tun:

Enum bit = (Enum)(object)curValueBit; 

Jon Skeet ein Projekt unconstrained melody genannt hat, dass Mit dieser Option können Sie die Enum-Abhängigkeit nach der Kompilierung hinzufügen, indem Sie die IL neu schreiben. Dies funktioniert, weil die CLR eine solche Einschränkung unterstützt, obwohl C# dies nicht tut.

Ein anderer Gedanke: Es wird effizienter, den Rückgabewert von GetValues ​​zu T[] direkt zu werfen:

foreach(T curValueBit in (T[])Enum.GetValues(typeof (T))) 
+0

maske.ToString() ist in der Tat, was ich endlich wollte (obwohl ich lieber eine flexiblere Lösung hätte). Es sieht so aus, als ob ein sehr ähnlicher Code im Framework implementiert ist, um ein solches Ergebnis zu ermöglichen. Ich würde es gerne sehen :) – PPC

+0

Ich bin auch kein Fan von der 'ReadyAndWaiting' Lösung: meine echte enum hat 14 Flags, und ich werde nicht alle möglichen Stufen mit so langen Namen implementieren :) – PPC

+0

Last Könnten Sie dem T [] nicht noch etwas mehr erklären? Performance-Probleme? – PPC

1

Was passiert, wenn nur so etwas tun:

public static IEnumerable<T> MaskToList<T>(Enum mask) 
{ 
if (typeof(T).IsSubclassOf(typeof(Enum)) == false) 
    throw new ArgumentException(); 

    List<T> toreturn = new List<T>(100); 

    foreach(T curValueBit in Enum.GetValues(typeof (T)).Cast<T>()) 
    { 
    Enum bit = (curValueBit as Enum); // The only difference is actually here, 
             // use "as", instead of (Enum) cast 

    if (mask.HasFlag(bit)) 
     toreturn.Add(curValueBit); 
    } 

    return toreturn; 
} 

Als as hat nicht Kompilierzeitprüfung Compiler hier "glauben" Sie einfach, in der Hoffnung, dass Sie wissen, was Sie tun, so dass die Kompilierzeit Fehler nicht ausgelöst.

+0

Warum nicht 'foreach (Enum curValueBit in (T []) Enum.GetValues ​​(typeof (T)))'? Auch für die Nützlichkeit als eine Erweiterung kann es besser sein, T-Maske statt Enum-Maske – Random832

+0

@ Random832 nehmen: haben Sie es versucht? – Tigran

+0

Die foreach-Anweisung funktioniert, obwohl der Hauptteil der Schleife geändert werden muss und unterschiedliche Leistungsmerkmale aufweisen kann. Ich vermute, der Cast zu T [] statt .Cast ist immer besser. Ich wäre besorgt über den Fall von phoog mit definierten Werten, die mehr als eine Flagge haben. Was macht HasFlag in diesem Fall? – Random832

2

Aufbauend auf Gabe's answer Ich kam auf diese:

public static class EnumHelper<T> 
    where T : struct 
{ 
    // ReSharper disable StaticFieldInGenericType 
    private static readonly Enum[] Values; 
    // ReSharper restore StaticFieldInGenericType 
    private static readonly T DefaultValue; 

    static EnumHelper() 
    { 
     var type = typeof(T); 
     if (type.IsSubclassOf(typeof(Enum)) == false) 
     { 
      throw new ArgumentException(); 
     } 
     Values = Enum.GetValues(type).Cast<Enum>().ToArray(); 
     DefaultValue = default(T); 
    } 

    public static T[] MaskToList(Enum mask, bool ignoreDefault = true) 
    { 
     var q = Values.Where(mask.HasFlag); 
     if (ignoreDefault) 
     { 
      q = q.Where(v => !v.Equals(DefaultValue)); 
     } 
     return q.Cast<T>().ToArray(); 
    } 
} 

ich die Dinge ein wenig anders ausgedrückt, nämlich organisierte ich die Typprüfung (dh die Überprüfung, dass T tatsächlich eine Aufzählung ist) und das Ermitteln der Aufzählungswerte im statischen Konstruktor, so dass dies nur einmal erfolgt (dies wäre eine Leistungsverbesserung).

Eine andere Sache, fügte ich einen optionalen Parameter hinzu, so dass Sie die typische "Null"/"None"/"NotApplicable"/"Undefined"/etc Wert der Aufzählung ignorieren können.

Verwandte Themen