2008-08-10 8 views
79

Was ich tun möchte, ist so etwas wie folgt: Ich habe enums mit kombinierten markierten Werten.Wer kennt eine gute Problemumgehung für das Fehlen einer enum generischen Einschränkung?

public static class EnumExtension 
{ 
    public static bool IsSet<T>(this T input, T matchTo) 
     where T:enum //the constraint I want that doesn't exist in C#3 
    {  
     return (input & matchTo) != 0; 
    } 
} 

So könnte ich tun:

MyEnum tester = MyEnum.FlagA | MyEnum.FlagB 

if(tester.IsSet(MyEnum.FlagA)) 
    //act on flag a 

Leider C# 's generic wo Einschränkungen keine Enum Einschränkung haben, nur Klasse und Struktur. C# sieht Enums nicht als Strukturen (obwohl sie Werttypen sind), daher kann ich keine Erweiterungstypen wie diese hinzufügen.

Wer kennt einen Workaround?

+2

Keith: Download-Version 0.0.0.2 von UnconstrainedMelody - Ich habe HasAll und HasAny implementiert. Genießen. –

+0

Was meinen Sie mit "C# sieht Enums als Strukturen nicht"? Sie können Aufzählungstypen als Typparameter verwenden, die auf "struct" beschränkt sind. – Timwi

+0

Überprüfen Sie diesen Artikel hier: http://www.codeproject.com/KB/cs/ExtendEnum.aspx 'IsValidEnumValue' oder 'IsFlagsEnumDefined' Methoden sind wahrscheinlich die Antwort auf Ihre Frage. – dmihailescu

Antwort

46

EDIT: Dies ist jetzt live in der Version 0.0.0.2 von UnconstrainedMelody.

(wie auf meinem blog post about enum constraints angefordert. Ich habe die grundlegenden Fakten auch weiter unten im Interesse einer eigenständigen Antwort.)

Die beste Lösung ist zu warten, bis ich es in UnconstrainedMelody aufzunehmen. Dies ist eine Bibliothek, die C# Code mit "falschen" Constraints wie

where T : struct, IEnumConstraint 

und wandelt es in

where T : struct, System.Enum 

über einen postbuild Schritt erfolgt.

Es sollte nicht zu schwer sein zu schreiben IsSet ... obwohl Catering für beide Int64 -basierte und UInt64-basierte Flags könnte der schwierige Teil sein. (Ich rieche einige Hilfsmethoden kommen auf, im Grunde erlaubt mir, alle Flaggen Enum zu behandeln, als ob es einen Basistyp von UInt64 hatte.)

Was würden Sie das Verhalten sein wollen, wenn Sie

tester.IsSet(MyFlags.A | MyFlags.C) 

genannt ? Sollte es überprüfen, dass alle die angegebenen Flags gesetzt sind? Das wäre meine Erwartung.

Ich werde versuchen, dies auf dem Heimweg heute Abend zu tun ... Ich hoffe, einen schnellen Blitz auf nützliche Enum-Methoden zu haben, um die Bibliothek schnell auf einen brauchbaren Standard zu bringen, dann ein wenig entspannen.

EDIT: Ich bin übrigens nicht sicher über IsSet als ein Name.Optionen:

  • Inklusive
  • Enthält
  • hasFlag (oder HasFlags)
  • IsSet (es ist sicherlich eine Option)

Gedanken willkommen. Ich bin sicher, dass es eine Weile der Satz, bevor etwas in Stein sowieso ...


oder legt ihn als Patch sein wird, natürlich ...

+0

Ich nehme an, wenn mehrere Flags übergeben werden, sollte für alle von ihnen überprüfen. Meine eigentliche Lösung dafür (zurück in 2008, als ich es fragte) war eine Template-Erweiterung-Methode für jede Flags Enum - chaotisch, aber funktioniert. Ich habe mich nie um die Überprüfung mehrerer Flags gekümmert, da alle Checks, die wir haben, für ein einzelnes Flag sind - nicht ein solches Problem im internen Code, sondern etwas, das in einer gemeinsam genutzten Bibliothek berücksichtigt werden müsste. – Keith

+1

Sie mussten gehen und PostSharp LOL erwähnen: o http://www.postsharp.org/blog/generic-constraints-for-enums-and-delegates –

+0

Ich würde die Flags-Terminologie einfach verwenden, weil es bereits in .NET existiert (Siehe 'FlagsAttribute'.) Ich sehe hier zwei explizite Namen:' HasAnyFlag' und 'HasAllFlags'. Sie können auf "HasFlag" und "HasFlags" verkürzt werden. Ich kann nicht sagen, welches ist das Beste, es ist eine Frage des Geschmacks, denke ich. – Blixt

16

Darren, das würde funktionieren, wenn die Typen spezifische Aufzählungen sind - für die allgemeinen Aufzählungen Sie arbeiten müssen, um sie zu ints werfen (oder wahrscheinlicher uint) die boolean Mathematik zu tun:

public static bool IsSet(this Enum input, Enum matchTo) 
{ 
    return (Convert.ToUInt32(input) & Convert.ToUInt32(matchTo)) != 0; 
} 
+1

Und wenn Sie eine lächerliche Anzahl von Flags haben, können Sie GetTypeCode() für die Argumente aufrufen und Convert.ToUint64() – Kit

+0

Awesome, die Kombination von "Enum" und "Convert.ToUInt32" habe ich nirgendwo anders gefunden. AFAIK, es ist die einzige anständige Pre-Net-4-Lösung, die auch in VB funktioniert. Wenn "matchTo" mehrere Flag-Bits hat, dann ersetzen Sie "! = 0" durch "== Convert.ToUInt32 (matchTo)". – ToolmakerSteve

+1

Beachten Sie, dass 'Convert.ToUInt32', das mit einer Enumeration verwendet wird, die' Convert.ToUInt32 (object) '- Überladung verwendet, was bedeutet, dass CLR diese Werte zuerst einkistet, bevor sie an die' ToUInt32'-Methode übergeben wird. In den meisten Fällen ist das egal, aber es ist gut zu wissen, dass Sie den GC ziemlich beschäftigt halten, wenn Sie so etwas verwenden, um Millionen von Enums pro Sekunde zu parsen. – Groo

1

Ihre ursprüngliche Verwendung Code innerhalb der Methode, die Sie auch Reflexion verwenden können, dass T zu testen, ist eine Enumeration:

public static class EnumExtension 
{ 
    public static bool IsSet<T>(this T input, T matchTo) 
    { 
     if (!typeof(T).IsEnum) 
     { 
      throw new ArgumentException("Must be an enum", "input"); 
     } 
     return (input & matchTo) != 0; 
    } 
} 
+2

Danke, aber das verwandelt ein Kompilierzeitproblem (die WHERE-Einschränkung) in eine Laufzeit (Ihre Ausnahme). Außerdem müssten Sie die Eingaben immer noch in Ints konvertieren, bevor Sie etwas damit anfangen könnten. – Keith

3

So wie ich es tun, eine Struktur Zwang gesetzt wird, dann überprüfen Sie, dass T eine Enumeration zur Laufzeit ist. Dies beseitigt das Problem nicht vollständig, aber es reduziert es etwas

+7

wo T: struct, IComparable, IFormattable, IConvertible - das ist die nächste, die Sie zu enum bekommen können :) – Kit

9

Eigentlich ist es möglich, mit einem hässlichen Trick. Es kann jedoch nicht für Erweiterungsmethoden verwendet werden.

public abstract class Enums<Temp> where Temp : class { 
    public static TEnum Parse<TEnum>(string name) where TEnum : struct, Temp { 
     return (TEnum)Enum.Parse(typeof(TEnum), name); 
    } 
} 
public abstract class Enums : Enums<Enum> { } 

Enums.IsSet<DateTimeKind>("Local") 

Wenn Sie möchten, können Sie Enums<Temp> einen privaten Konstruktor und eine öffentliche verschachtelte abstrakte vererbten Klasse mit Temp als Enum geben kann, erbte Versionen für nicht-Aufzählungen zu verhindern.

1

Hier ist ein Code, den ich gerade gemacht habe, der so funktioniert, wie du willst, ohne etwas zu verrückt machen zu müssen. Es ist nicht nur auf Enums beschränkt, die als Flags gesetzt sind, aber es könnte immer ein Check eingefügt werden, wenn es nötig ist.

public static class EnumExtensions 
{ 
    public static bool ContainsFlag(this Enum source, Enum flag) 
    { 
     var sourceValue = ToUInt64(source); 
     var flagValue = ToUInt64(flag); 

     return (sourceValue & flagValue) == flagValue; 
    } 

    public static bool ContainsAnyFlag(this Enum source, params Enum[] flags) 
    { 
     var sourceValue = ToUInt64(source); 

     foreach (var flag in flags) 
     { 
      var flagValue = ToUInt64(flag); 

      if ((sourceValue & flagValue) == flagValue) 
      { 
       return true; 
      } 
     } 

     return false; 
    } 

    // found in the Enum class as an internal method 
    private static ulong ToUInt64(object value) 
    { 
     switch (Convert.GetTypeCode(value)) 
     { 
      case TypeCode.SByte: 
      case TypeCode.Int16: 
      case TypeCode.Int32: 
      case TypeCode.Int64: 
       return (ulong)Convert.ToInt64(value, CultureInfo.InvariantCulture); 

      case TypeCode.Byte: 
      case TypeCode.UInt16: 
      case TypeCode.UInt32: 
      case TypeCode.UInt64: 
       return Convert.ToUInt64(value, CultureInfo.InvariantCulture); 
     } 

     throw new InvalidOperationException("Unknown enum type."); 
    } 
} 
4

Diese die ursprüngliche Frage nicht beantworten, aber es gibt jetzt eine Methode in .NET 4 genannt Enum.HasFlag was tut, was Sie in Ihrem Beispiel

+0

Upvoted, da zu diesem Zeitpunkt die meisten Benutzer .NET 4 (oder höher) verwenden sollten und sie diese Methode verwenden sollten, anstatt zu versuchen, sie gemeinsam zu hacken. – CptRobby

+0

Upvoted. Ihre Lösung verwendet jedoch das Boxen des Arguments "flag". .NET 4.0 ist jetzt fünf Jahre alt. –

8

Sie erreichen dies mit IL Weben versuchen zu tun und ExtraConstraints

Hier können Sie diesen Code schreiben

public class Sample 
{ 
    public void MethodWithDelegateConstraint<[DelegateConstraint] T>() 
    {   
    } 
    public void MethodWithEnumConstraint<[EnumConstraint] T>() 
    { 
    } 
} 

Was

public class Sample 
{ 
    public void MethodWithDelegateConstraint<T>() where T: Delegate 
    { 
    } 

    public void MethodWithEnumConstraint<T>() where T: struct, Enum 
    { 
    } 
} 
0

zusammengestellt wird Ich wollte nur Enum als generische Einschränkung hinzuzufügen.

Während dies nur für eine kleine Helfer-Methode mit ExtraConstraints ist, ist ein bisschen zu viel Aufwand für mich.

Ich entschied mich, nur eine struct Einschränkung zu erstellen und eine Laufzeitprüfung für IsEnum hinzufügen. Um eine Variable von T nach Enum zu konvertieren, werfe ich sie zuerst auf das Objekt.

public static Converter<T, string> CreateConverter<T>() where T : struct 
    { 
     if (!typeof(T).IsEnum) throw new ArgumentException("Given Type is not an Enum"); 
     return new Converter<T, string>(x => ((Enum)(object)x).GetEnumDescription()); 
    } 
0

wenn jemand generic IsSet muss (erstellt aus der Box auf Fly könnte auf verbessert werden), und oder String Enum onfly Umwandlung (die EnumConstraint verwendet vorgestellt unten):

public class TestClass 
    { } 

    public struct TestStruct 
    { } 

    public enum TestEnum 
    { 
    e1,  
    e2, 
    e3 
    } 

    public static class TestEnumConstraintExtenssion 
    { 

    public static bool IsSet<TEnum>(this TEnum _this, TEnum flag) 
     where TEnum : struct 
    { 
     return (((uint)Convert.ChangeType(_this, typeof(uint))) & ((uint)Convert.ChangeType(flag, typeof(uint)))) == ((uint)Convert.ChangeType(flag, typeof(uint))); 
    } 

    //public static TestClass ToTestClass(this string _this) 
    //{ 
    // // #generates compile error (so no missuse) 
    // return EnumConstraint.TryParse<TestClass>(_this); 
    //} 

    //public static TestStruct ToTestStruct(this string _this) 
    //{ 
    // // #generates compile error (so no missuse) 
    // return EnumConstraint.TryParse<TestStruct>(_this); 
    //} 

    public static TestEnum ToTestEnum(this string _this) 
    { 
     // #enum type works just fine (coding constraint to Enum type) 
     return EnumConstraint.TryParse<TestEnum>(_this); 
    } 

    public static void TestAll() 
    { 
     TestEnum t1 = "e3".ToTestEnum(); 
     TestEnum t2 = "e2".ToTestEnum(); 
     TestEnum t3 = "non existing".ToTestEnum(); // default(TestEnum) for non existing 

     bool b1 = t3.IsSet(TestEnum.e1); // you can ommit type 
     bool b2 = t3.IsSet<TestEnum>(TestEnum.e2); // you can specify explicite type 

     TestStruct t; 
     // #generates compile error (so no missuse) 
     //bool b3 = t.IsSet<TestEnum>(TestEnum.e1); 

    } 

    } 

Wenn jemand noch benötigt Beispiel heiß, um Enum Codierungsbeschränkung zu erstellen:

hoffe das hilft jemandem.