it-swarm-eu.dev

Erstellen Sie eine generische Methode, die T auf eine Aufzählung beschränkt

Ich erstelle eine Funktion, um das Enum.Parse -Konzept dahingehend zu erweitern

  • Ermöglicht das Analysieren eines Standardwerts für den Fall, dass kein Enum-Wert gefunden wird
  • Groß- und Kleinschreibung wird nicht berücksichtigt

Also habe ich folgendes geschrieben:

public static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
{
    if (string.IsNullOrEmpty(value)) return defaultValue;
    foreach (T item in Enum.GetValues(typeof(T)))
    {
        if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
    }
    return defaultValue;
}

Ich erhalte eine Fehlerbedingung, die nicht der Sonderklasse System.Enum entsprechen kann.

Fair genug, aber gibt es eine Problemumgehung, um eine generische Aufzählung zuzulassen, oder muss ich die Funktion Parse imitieren und einen Typ als Attribut übergeben, wodurch die hässliche Boxing-Anforderung an Ihren Code erzwungen wird.

EDIT Alle Vorschläge unten wurden sehr geschätzt, danke.

Ich habe mich entschieden (Ich habe die Schleife verlassen, um Groß- und Kleinschreibung zu vermeiden. Ich verwende dies beim Parsen von XML.)

public static class EnumUtils
{
    public static T ParseEnum<T>(string value, T defaultValue) where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");
        if (string.IsNullOrEmpty(value)) return defaultValue;

        foreach (T item in Enum.GetValues(typeof(T)))
        {
            if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        }
        return defaultValue;
    }
}

BEARBEITEN: (16. Februar 2015) Julien Lebosquain hat kürzlich eine vom Compiler erzwungene typsichere generische Lösung in MSIL oder F # unten veröffentlicht , was einen Blick wert ist, und eine Gegenstimme. Ich werde diese Änderung entfernen, wenn die Lösung weiter oben auf der Seite angezeigt wird.

1084
johnc

Da Enum Type die IConvertible Schnittstelle implementiert, sollte eine bessere Implementierung ungefähr so ​​aussehen:

public T GetEnumFromString<T>(string value) where T : struct, IConvertible
{
   if (!typeof(T).IsEnum) 
   {
      throw new ArgumentException("T must be an enumerated type");
   }

   //...
}

Dies erlaubt weiterhin die Übergabe von Werttypen, die IConvertible implementieren. Die Chancen sind jedoch selten.

948
Vivek

Diese Funktion wird endlich in C # 7.3 unterstützt!

Das folgende Snippet (aus die Dotnet-Beispiele ) zeigt, wie:

public static Dictionary<int, string> EnumNamedValues<T>() where T : System.Enum
{
    var result = new Dictionary<int, string>();
    var values = Enum.GetValues(typeof(T));

    foreach (int item in values)
        result.Add(item, Enum.GetName(typeof(T), item));
    return result;
}

Stellen Sie sicher, dass Ihre Sprachversion in Ihrem C # -Projekt auf Version 7.3 eingestellt ist.


Ursprüngliche Antwort unten:

Ich bin zu spät zum Spiel gekommen, aber ich habe es als Herausforderung angesehen, zu sehen, wie es gemacht werden kann. Es ist in C # (oder VB.NET nicht möglich, aber scrollen Sie nach unten für F #), aber ist in MSIL möglich . Ich habe dieses kleine ... Ding geschrieben

// license: http://www.Apache.org/licenses/LICENSE-2.0.html
.Assembly MyThing{}
.class public abstract sealed MyThing.Thing
       extends [mscorlib]System.Object
{
  .method public static !!T  GetEnumFromString<valuetype .ctor ([mscorlib]System.Enum) T>(string strValue,
                                                                                          !!T defaultValue) cil managed
  {
    .maxstack  2
    .locals init ([0] !!T temp,
                  [1] !!T return_value,
                  [2] class [mscorlib]System.Collections.IEnumerator enumerator,
                  [3] class [mscorlib]System.IDisposable disposer)
    // if(string.IsNullOrEmpty(strValue)) return defaultValue;
    ldarg strValue
    call bool [mscorlib]System.String::IsNullOrEmpty(string)
    brfalse.s HASVALUE
    br RETURNDEF         // return default it empty

    // foreach (T item in Enum.GetValues(typeof(T)))
  HASVALUE:
    // Enum.GetValues.GetEnumerator()
    ldtoken !!T
    call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    call class [mscorlib]System.Array [mscorlib]System.Enum::GetValues(class [mscorlib]System.Type)
    callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Array::GetEnumerator() 
    stloc enumerator
    .try
    {
      CONDITION:
        ldloc enumerator
        callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
        brfalse.s LEAVE

      STATEMENTS:
        // T item = (T)Enumerator.Current
        ldloc enumerator
        callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
        unbox.any !!T
        stloc temp
        ldloca.s temp
        constrained. !!T

        // if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        callvirt instance string [mscorlib]System.Object::ToString()
        callvirt instance string [mscorlib]System.String::ToLower()
        ldarg strValue
        callvirt instance string [mscorlib]System.String::Trim()
        callvirt instance string [mscorlib]System.String::ToLower()
        callvirt instance bool [mscorlib]System.String::Equals(string)
        brfalse.s CONDITION
        ldloc temp
        stloc return_value
        leave.s RETURNVAL

      LEAVE:
        leave.s RETURNDEF
    }
    finally
    {
        // ArrayList's Enumerator may or may not inherit from IDisposable
        ldloc enumerator
        isinst [mscorlib]System.IDisposable
        stloc.s disposer
        ldloc.s disposer
        ldnull
        ceq
        brtrue.s LEAVEFINALLY
        ldloc.s disposer
        callvirt instance void [mscorlib]System.IDisposable::Dispose()
      LEAVEFINALLY:
        endfinally
    }

  RETURNDEF:
    ldarg defaultValue
    stloc return_value

  RETURNVAL:
    ldloc return_value
    ret
  }
} 

Was eine Funktion erzeugt, die so aussehen würde , wenn sie gültig wäre C #:

T GetEnumFromString<T>(string valueString, T defaultValue) where T : Enum

Dann mit folgendem C # -Code:

using MyThing;
// stuff...
private enum MyEnum { Yes, No, Okay }
static void Main(string[] args)
{
    Thing.GetEnumFromString("No", MyEnum.Yes); // returns MyEnum.No
    Thing.GetEnumFromString("Invalid", MyEnum.Okay);  // returns MyEnum.Okay
    Thing.GetEnumFromString("AnotherInvalid", 0); // compiler error, not an Enum
}

Leider bedeutet dies, dass dieser Teil Ihres Codes in MSIL anstatt in C # geschrieben ist. Der einzige zusätzliche Vorteil besteht darin, dass Sie diese Methode durch System.Enum einschränken können. Es ist auch eine Art Mist, weil es in einer separaten Assembly kompiliert wird. Dies bedeutet jedoch nicht, dass Sie es auf diese Weise bereitstellen müssen.

Entfernen Sie die Zeile .Assembly MyThing{} und rufen Sie ilasm wie folgt auf:

ilasm.exe /DLL /OUTPUT=MyThing.netmodule

sie erhalten ein Netzmodul anstelle einer Assembly.

Leider unterstützt VS2010 (und früher natürlich nicht) das Hinzufügen von NetModule-Referenzen, was bedeutet, dass Sie es beim Debuggen in zwei separaten Assemblys belassen müssen. Sie können sie nur als Teil Ihrer Assembly hinzufügen, indem Sie csc.exe selbst mit dem Befehlszeilenargument /addmodule:{files} ausführen. Es wäre nicht zu schmerzhaft in einem MSBuild-Skript. Wenn Sie mutig oder dumm sind, können Sie natürlich jedes Mal manuell csc ausführen. Und es wird sicherlich komplizierter, da mehrere Baugruppen Zugriff darauf benötigen.

So kann es in .Net erfolgen. Lohnt sich der zusätzliche Aufwand? Ähm, ich schätze, ich lasse dich darüber entscheiden.


F # -Lösung als Alternative

Extra Credit: Es stellt sich heraus, dass eine generische Einschränkung für enum in mindestens einer anderen .NET-Sprache neben MSIL: F # möglich ist.

type MyThing =
    static member GetEnumFromString<'T when 'T :> Enum> str defaultValue: 'T =
        /// protect for null (only required in interop with C#)
        let str = if isNull str then String.Empty else str

        Enum.GetValues(typedefof<'T>)
        |> Seq.cast<_>
        |> Seq.tryFind(fun v -> String.Compare(v.ToString(), str.Trim(), true) = 0)
        |> function Some x -> x | None -> defaultValue

Diese Sprache ist einfacher zu warten, da sie eine bekannte Sprache mit vollständiger Visual Studio IDE -Unterstützung ist. Sie benötigen jedoch weiterhin ein separates Projekt in Ihrer Lösung. Es erzeugt jedoch natürlich eine beträchtlich andere IL (der Code ist sehr unterschiedlich) und stützt sich auf die FSharp.Core -Bibliothek, die genau wie jede andere externe Bibliothek benötigt wird Werden Sie Teil Ihrer Distribution.

Hier ist, wie Sie es verwenden können (im Grunde das gleiche wie die MSIL-Lösung), und um zu zeigen, dass es auf anderen Strukturen auch korrekt fehlschlägt:

// works, result is inferred to have type StringComparison
var result = MyThing.GetEnumFromString("OrdinalIgnoreCase", StringComparison.Ordinal);
// type restriction is recognized by C#, this fails at compile time
var result = MyThing.GetEnumFromString("OrdinalIgnoreCase", 42);
567

C # ≥ 7.3

Ab C # 7.3 (verfügbar mit Visual Studio 2017 ≥ v15.7) ist dieser Code jetzt vollständig gültig:

public static TEnum Parse<TEnum>(string value)
where TEnum : struct, Enum { ... }

C # ≤ 7,2

Sie können eine echte Enum-Einschränkung durch einen Compiler erzwingen lassen, indem Sie die Vererbung von Einschränkungen missbrauchen. Der folgende Code gibt gleichzeitig eine class und eine struct Einschränkung an:

public abstract class EnumClassUtils<TClass>
where TClass : class
{

    public static TEnum Parse<TEnum>(string value)
    where TEnum : struct, TClass
    {
        return (TEnum) Enum.Parse(typeof(TEnum), value);
    }

}

public class EnumUtils : EnumClassUtils<Enum>
{
}

Verwendungszweck:

EnumUtils.Parse<SomeEnum>("value");

Hinweis: Dies wird speziell in der C # 5.0-Sprachspezifikation angegeben:

Wenn der Typparameter S vom Typparameter T abhängt, gilt Folgendes: [...] Für S gilt die Werttypbeschränkung und für T die Referenztypbeschränkung. Dies begrenzt T effektiv auf die Typen System.Object, System.ValueType, System.Enum und jeden Schnittstellentyp.

192

Bearbeiten

Die Frage wurde jetzt hervorragend von Julien Lebosquain beantwortet. Ich möchte seine Antwort auch mit ignoreCase, defaultValue und optionalen Argumenten erweitern, während ich TryParse und ParseOrDefault hinzufüge.

public abstract class ConstrainedEnumParser<TClass> where TClass : class
// value type constraint S ("TEnum") depends on reference type T ("TClass") [and on struct]
{
    // internal constructor, to prevent this class from being inherited outside this code
    internal ConstrainedEnumParser() {}
    // Parse using pragmatic/adhoc hard cast:
    //  - struct + class = enum
    //  - 'guaranteed' call from derived <System.Enum>-constrained type EnumUtils
    public static TEnum Parse<TEnum>(string value, bool ignoreCase = false) where TEnum : struct, TClass
    {
        return (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase);
    }
    public static bool TryParse<TEnum>(string value, out TEnum result, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T
    {
        var didParse = Enum.TryParse(value, ignoreCase, out result);
        if (didParse == false)
        {
            result = defaultValue;
        }
        return didParse;
    }
    public static TEnum ParseOrDefault<TEnum>(string value, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T
    {
        if (string.IsNullOrEmpty(value)) { return defaultValue; }
        TEnum result;
        if (Enum.TryParse(value, ignoreCase, out result)) { return result; }
        return defaultValue;
    }
}

public class EnumUtils: ConstrainedEnumParser<System.Enum>
// reference type constraint to any <System.Enum>
{
    // call to parse will then contain constraint to specific <System.Enum>-class
}

Anwendungsbeispiele:

WeekDay parsedDayOrArgumentException = EnumUtils.Parse<WeekDay>("monday", ignoreCase:true);
WeekDay parsedDayOrDefault;
bool didParse = EnumUtils.TryParse<WeekDay>("clubs", out parsedDayOrDefault, ignoreCase:true);
parsedDayOrDefault = EnumUtils.ParseOrDefault<WeekDay>("friday", ignoreCase:true, defaultValue:WeekDay.Sunday);

Alt

Meine alten Verbesserungen an Viveks Antwort unter Verwendung der Kommentare und 'neuen' Entwicklungen:

  • verwenden Sie TEnum, um den Benutzern Klarheit zu verschaffen
  • füge weitere Interface-Constraints für zusätzliche Constraint-Checks hinzu
  • let TryParse handle ignoreCase mit dem vorhandenen Parameter (eingeführt in VS2010/.Net 4)
  • verwenden Sie optional den generischen default value (eingeführt in VS2005/.Net 2)
  • verwenden Sie optionale Argumente (eingeführt in VS2010/.Net 4) mit Standardwerten für defaultValue und ignoreCase

ergebend:

public static class EnumUtils
{
    public static TEnum ParseEnum<TEnum>(this string value,
                                         bool ignoreCase = true,
                                         TEnum defaultValue = default(TEnum))
        where TEnum : struct,  IComparable, IFormattable, IConvertible
    {
        if ( ! typeof(TEnum).IsEnum) { throw new ArgumentException("TEnum must be an enumerated type"); }
        if (string.IsNullOrEmpty(value)) { return defaultValue; }
        TEnum lResult;
        if (Enum.TryParse(value, ignoreCase, out lResult)) { return lResult; }
        return defaultValue;
    }
}
30
Yahoo Serious

Sie können einen statischen Konstruktor für die Klasse definieren, der prüft, ob der Typ T eine Aufzählung ist, und eine Ausnahme auslöst, wenn dies nicht der Fall ist. Dies ist die Methode, die Jeffery Richter in seinem Buch CLR über C # erwähnt.

internal sealed class GenericTypeThatRequiresAnEnum<T> {
    static GenericTypeThatRequiresAnEnum() {
        if (!typeof(T).IsEnum) {
        throw new ArgumentException("T must be an enumerated type");
        }
    }
}

Dann können Sie in der Analysemethode einfach Enum.Parse (typeof (T), input, true) verwenden, um von string in enum zu konvertieren. Der letzte wahre Parameter dient zum Ignorieren des Eingangs.

18
Karg

Es sollte auch berücksichtigt werden, dass die Veröffentlichung von C # 7.3 mit Enum-Einschränkungen sofort unterstützt wird, ohne dass zusätzliche Prüfungen durchgeführt werden müssen.

Wenn Sie also die Sprachversion Ihres Projekts in C # 7.3 geändert haben, funktioniert der folgende Code einwandfrei:

    private static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
    {
        // Your code goes here...
    }

Falls Sie nicht wissen, wie Sie die Sprachversion in C # 7.3 ändern können, sehen Sie sich den folgenden Screenshot an: enter image description here

EDIT 1 - Erforderliche Visual Studio-Version und unter Berücksichtigung von ReSharper

Damit Visual Studio die neue Syntax erkennt, benötigen Sie mindestens Version 15.7. Sie finden dies auch in den Versionshinweisen von Microsoft, siehe Visual Studio 2017 15.7 Versionshinweise . Vielen Dank an @MohamedElshawaf für den Hinweis auf diese gültige Frage.

Bitte beachten Sie auch, dass in meinem Fall ReSharper 2018.1 zum Zeitpunkt des Schreibens dieses EDIT C # 7.3 noch nicht unterstützt. Wenn ReSharper aktiviert ist, wird die Enum-Einschränkung als Fehler hervorgehoben. Kann nicht 'System.Array', 'System.Delegate', 'System.Enum', 'System.ValueType', 'object' als verwenden Typparametereinschränkung . ReSharper schlägt als schnelle Lösung vor, die 'Enum'-Einschränkung des Typparameters T der Methode zu entfernen.

Wenn Sie ReSharper jedoch unter Extras -> Optionen -> ReSharper Ultimate -> Allgemein vorübergehend deaktivieren, wird bei Verwendung von VS 15.7 oder höher eine einwandfreie Syntax angezeigt und C # 7.3 oder höher.

15
baumgarb

Ich habe das Sample von dimarzionist modifiziert. Diese Version funktioniert nur mit Enums und lässt keine Strukturen durch.

public static T ParseEnum<T>(string enumString)
    where T : struct // enum 
    {
    if (String.IsNullOrEmpty(enumString) || !typeof(T).IsEnum)
       throw new Exception("Type given must be an Enum");
    try
    {

       return (T)Enum.Parse(typeof(T), enumString, true);
    }
    catch (Exception ex)
    {
       return default(T);
    }
}
11
Bivoauc

Ich habe versucht, den Code ein wenig zu verbessern:

public T LoadEnum<T>(string value, T defaultValue = default(T)) where T : struct, IComparable, IFormattable, IConvertible
{
    if (Enum.IsDefined(typeof(T), value))
    {
        return (T)Enum.Parse(typeof(T), value, true);
    }
    return defaultValue;
}
9
Martin

Ich habe bestimmte Anforderungen, bei denen ich die Aufzählung für Text verwenden muss, der dem Aufzählungswert zugeordnet ist. Wenn ich zum Beispiel den Fehlertyp mit enum spezifiziere, ist es erforderlich, Fehlerdetails zu beschreiben.

public static class XmlEnumExtension
{
    public static string ReadXmlEnumAttribute(this Enum value)
    {
        if (value == null) throw new ArgumentNullException("value");
        var attribs = (XmlEnumAttribute[]) value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof (XmlEnumAttribute), true);
        return attribs.Length > 0 ? attribs[0].Name : value.ToString();
    }

    public static T ParseXmlEnumAttribute<T>(this string str)
    {
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            var attribs = (XmlEnumAttribute[])item.GetType().GetField(item.ToString()).GetCustomAttributes(typeof(XmlEnumAttribute), true);
            if(attribs.Length > 0 && attribs[0].Name.Equals(str)) return item;
        }
        return (T)Enum.Parse(typeof(T), str, true);
    }
}

public enum MyEnum
{
    [XmlEnum("First Value")]
    One,
    [XmlEnum("Second Value")]
    Two,
    Three
}

 static void Main()
 {
    // Parsing from XmlEnum attribute
    var str = "Second Value";
    var me = str.ParseXmlEnumAttribute<MyEnum>();
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
    // Parsing without XmlEnum
    str = "Three";
    me = str.ParseXmlEnumAttribute<MyEnum>();
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
    me = MyEnum.One;
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
}
5
Sunny Rajwadi

Hoffe das ist hilfreich:

public static TValue ParseEnum<TValue>(string value, TValue defaultValue)
                  where TValue : struct // enum 
{
      try
      {
            if (String.IsNullOrEmpty(value))
                  return defaultValue;
            return (TValue)Enum.Parse(typeof (TValue), value);
      }
      catch(Exception ex)
      {
            return defaultValue;
      }
}
4
dimarzionist

Das ist meine Einstellung dazu. Kombiniert aus den Antworten und MSDN

public static TEnum ParseToEnum<TEnum>(this string text) where TEnum : struct, IConvertible, IComparable, IFormattable
{
    if (string.IsNullOrEmpty(text) || !typeof(TEnum).IsEnum)
        throw new ArgumentException("TEnum must be an Enum type");

    try
    {
        var enumValue = (TEnum)Enum.Parse(typeof(TEnum), text.Trim(), true);
        return enumValue;
    }
    catch (Exception)
    {
        throw new ArgumentException(string.Format("{0} is not a member of the {1} enumeration.", text, typeof(TEnum).Name));
    }
}

MSDN-Quelle

3
KarmaEDV

Interessanterweise ist dies anscheinend in anderen Sprachen möglich (Managed C++, IL direkt).

Zitieren:

... Beide Einschränkungen erzeugen tatsächlich eine gültige AWL und können auch von C # verwendet werden, wenn sie in einer anderen Sprache geschrieben wurden (Sie können diese Einschränkungen in verwaltetem C++ oder in AWL deklarieren).

Wer weiß

3
Andrew Backer

Die vorhandenen Antworten gelten ab C # <= 7.2. Es gibt jedoch eine C # -Sprache Featureanforderung (gebunden an eine corefx Featureanforderung), um Folgendes zu ermöglichen;

public class MyGeneric<TEnum> where TEnum : System.Enum
{ }

Zum Zeitpunkt des Schreibens ist das Feature bei den Sprachentwicklungstreffen "In Diskussion".

EDIT

Gemäß den Informationen von nawfal wird dies in C # 7. eingeführt.

3
DiskJunky

Ich habe die Lösung von Vivek in einer Utility-Klasse zusammengefasst, die Sie wiederverwenden können. Bitte beachten Sie, dass Sie weiterhin Typeinschränkungen für Ihren Typ definieren sollten "where T: struct, IConvertible".

using System;

internal static class EnumEnforcer
{
    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="typeParameterName">Name of the type parameter.</param>
    /// <param name="methodName">Name of the method which accepted the parameter.</param>
    public static void EnforceIsEnum<T>(string typeParameterName, string methodName)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            string message = string.Format(
                "Generic parameter {0} in {1} method forces an enumerated type. Make sure your type parameter {0} is an enum.",
                typeParameterName,
                methodName);

            throw new ArgumentException(message);
        }
    }

    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="typeParameterName">Name of the type parameter.</param>
    /// <param name="methodName">Name of the method which accepted the parameter.</param>
    /// <param name="inputParameterName">Name of the input parameter of this page.</param>
    public static void EnforceIsEnum<T>(string typeParameterName, string methodName, string inputParameterName)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            string message = string.Format(
                "Generic parameter {0} in {1} method forces an enumerated type. Make sure your input parameter {2} is of correct type.",
                typeParameterName,
                methodName,
                inputParameterName);

            throw new ArgumentException(message);
        }
    }

    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="exceptionMessage">Message to show in case T is not an enum.</param>
    public static void EnforceIsEnum<T>(string exceptionMessage)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException(exceptionMessage);
        }
    }
}
1
niaher

Mir hat das immer gefallen (Sie können es entsprechend ändern):

public static IEnumerable<TEnum> GetEnumValues()
{
  Type enumType = typeof(TEnum);

  if(!enumType.IsEnum)
    throw new ArgumentException("Type argument must be Enum type");

  Array enumValues = Enum.GetValues(enumType);
  return enumValues.Cast<TEnum>();
}
1
Jeff

Wie bereits in anderen Antworten erwähnt; Während dies nicht im Quellcode ausgedrückt werden kann, kann es tatsächlich auf IL-Ebene durchgeführt werden. @Christopher Currens Antwort zeigt, wie die IL damit umgeht.

Mit Fody s Add-In ExtraConstraints.Fody gibt es eine sehr einfache Möglichkeit, dies mit Build-Tooling zu erreichen. Fügen Sie einfach die zugehörigen Nuget-Pakete (Fody, ExtraConstraints.Fody) zu Ihrem Projekt hinzu und fügen Sie die Einschränkungen wie folgt hinzu (Auszug aus der Readme-Datei von ExtraConstraints):

public void MethodWithEnumConstraint<[EnumConstraint] T>() {...}

public void MethodWithTypeEnumConstraint<[EnumConstraint(typeof(ConsoleColor))] T>() {...}

und Fody fügt die erforderliche IL hinzu, damit die Einschränkung vorhanden ist. Beachten Sie auch die zusätzliche Funktion zum Einschränken von Delegierten:

public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
{...}

public void MethodWithTypeDelegateConstraint<[DelegateConstraint(typeof(Func<int>))] T> ()
{...}

In Bezug auf Enums sollten Sie auch das hochinteressante Enums.NET beachten.

1

Ich habe eine Erweiterung Methode to get integer value from enum erstellt. Schauen Sie sich die Implementierung der Methode an

public static int ToInt<T>(this T soure) where T : IConvertible//enum
{
    if (typeof(T).IsEnum)
    {
        return (int) (IConvertible)soure;// the tricky part
    }
    //else
    //    throw new ArgumentException("T must be an enumerated type");
    return soure.ToInt32(CultureInfo.CurrentCulture);
}

das ist Gebrauch

MemberStatusEnum.Activated.ToInt()// using extension Method
(int) MemberStatusEnum.Activated //the ordinary way
1

Ich mochte Christopher Currens 'Lösung mit IL, aber für diejenigen, die sich nicht mit der kniffligen Aufgabe beschäftigen wollen, MSIL in ihren Erstellungsprozess einzubeziehen, habe ich eine ähnliche Funktion in C # geschrieben.

Beachten Sie jedoch, dass Sie keine generischen Einschränkungen wie where T : Enum verwenden können, da es sich bei Enum um einen speziellen Typ handelt. Daher muss ich prüfen, ob der angegebene generische Typ wirklich enum ist.

Meine Funktion ist:

public static T GetEnumFromString<T>(string strValue, T defaultValue)
{
    // Check if it realy enum at runtime 
    if (!typeof(T).IsEnum)
        throw new ArgumentException("Method GetEnumFromString can be used with enums only");

    if (!string.IsNullOrEmpty(strValue))
    {
        IEnumerator enumerator = Enum.GetValues(typeof(T)).GetEnumerator();
        while (enumerator.MoveNext())
        {
            T temp = (T)enumerator.Current;
            if (temp.ToString().ToLower().Equals(strValue.Trim().ToLower()))
                return temp;
        }
    }

    return defaultValue;
}
1
expert

Wenn es in Ordnung ist, Direct Casting später zu verwenden, können Sie die Basisklasse System.Enum in Ihrer Methode verwenden, wo immer dies erforderlich ist. Sie müssen nur die Typparameter sorgfältig ersetzen. Die Implementierung der Methode wäre also wie folgt:

public static class EnumUtils
{
    public static Enum GetEnumFromString(string value, Enum defaultValue)
    {
        if (string.IsNullOrEmpty(value)) return defaultValue;
        foreach (Enum item in Enum.GetValues(defaultValue.GetType()))
        {
            if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        }
        return defaultValue;
    }
}

Dann können Sie es wie folgt verwenden:

var parsedOutput = (YourEnum)EnumUtils.GetEnumFromString(someString, YourEnum.DefaultValue);
0
uluorta