Frage

Ich baue eine Funktion, um das Enum.Parse Konzept zu erweitern, dass

  • Ermöglicht ein Standardwert für den Fall analysiert werden, dass ein Enum-Wert nicht gefunden wird
  • Sie Groß- und Kleinschreibung

Also schrieb ich die folgende:

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 Fehler Constraint nicht Sonderklasse System.Enum sein kann.

Na gut, aber gibt es eine Abhilfe eine generische Enum zu ermöglichen, oder werde ich die Parse Funktion zu imitieren und einen Typ als Attribut übergeben, die die hässliche Box-Anforderung an Ihren Code zwingt.

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

Haben ließ sich auf (ich die Schleife verlassen haben Groß- und Kleinschreibung zu halten - ich bin mit diesem 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;
    }
}

EDIT: (16. Februar 2015) Julien Lebosquain hat vor kurzem geschrieben einen Compiler erzwungen typsichere generische Lösung in MSIL oder F # unten, was gut ist einen Blick wert, und ein upvote. Ich werde diese bearbeiten entfernen, wenn die Lösung weiter oben auf der Seite Blasen.

War es hilfreich?

Lösung

Da Enum Typ IConvertible Schnittstelle implementiert, eine bessere Umsetzung in etwa so sein sollte:

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 noch Gang Werttypen Umsetzung IConvertible. Die Chancen sind allerdings selten.

Andere Tipps

Diese Funktion wird schließlich C # 7.3 unterstützt in!

Der folgende Ausschnitt (von die dotnet Proben ) 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;
}

Seien Sie sicher, dass Ihre Sprachversion in Ihrem C # Projekt auf Version 7.3.


Original Antwort unter:

Ich bin auf das Spiel spät, aber ich nahm es als Herausforderung zu sehen, wie es getan werden könnte. Es ist nicht möglich, in C # (oder VB.NET, aber nach unten scrollen für F #), aber ist möglich in MSIL. Ich schrieb diese kleine .... Sache

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

, die eine Funktion erzeugt, dass würde so aussehen, wenn es gilt C #:

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

Dann mit dem folgenden 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, anstelle von C # in MSIL geschrieben, diesen Teil des Codes, die mit dem einzigen zusätzlichen Vorteil ist, dass Sie diese Methode durch System.Enum zu beschränken sind in der Lage. Es ist auch ein bisschen eine Enttäuschung, weil sie in einer separaten Assembly kompiliert wird. Allerdings bedeutet es nicht, dass Sie es auf diese Weise zu implementieren haben.

Durch die Linie .assembly MyThing{} Entfernen und Aufrufen ilasm wie folgt:

ilasm.exe /DLL /OUTPUT=MyThing.netmodule

Sie eine NetModule statt einer Baugruppe erhalten.

Leider VS2010 (und früher, natürlich) nicht unterstützt NetModule Referenzen hinzufügen, was bedeutet, würden Sie es in zwei separaten Baugruppen verlassen müssen, wenn Sie das Debuggen. Die einzige Möglichkeit, sie als Teil der Assembly hinzufügen würde, dich laufen Csc.exe das /addmodule:{files} Befehlszeilenargument verwenden. Es wäre nicht zu painful in einem MSBuild-Skript. Natürlich, wenn Sie mutig oder dumm sind, können Sie csc sich jedes Mal manuell ausgeführt werden. Und es wird sicherlich komplizierter als mehrere Baugruppen Zugang zu benötigen.

So kann es in .Net erfolgen. Lohnt es sich, den zusätzlichen Aufwand? Ähm, na ja, ich glaube, ich lasse Sie auf, dass man sich entscheiden.


F # Lösung als Alternative

Extra Credit: Es stellt sich heraus, dass eine allgemeine Beschränkung enum in mindestens einem anderen .NET-Sprache neben MSIL möglich ist. F #

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

Dieser ist leichter zu pflegen, da es sich um eine bekannte Sprache mit voller Visual Studio IDE-Unterstützung ist, aber immer noch ein separates Projekt in Ihrer Lösung für diese benötigen. Es ist jedoch natürlich produziert deutlich unterschiedlich IL (der Code ist sehr unterschiedlich), und es beruht auf der FSharp.Core Bibliothek, die, wie jede andere externe Bibliothek, muss einen Teil Ihrer Distribution entwickelt.

Hier ist, wie Sie es verwenden können (im Grunde die gleiche wie die MSIL-Lösung), und um zu zeigen, dass es korrekt auf sonst auch structs 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);

C # ≥ 7,3

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

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

C # ≤ 7,2

Sie können missbrauchen Zwang Vererbung einen echten Compiler erzwungen Enum-Einschränkung. Der folgende Code gibt sowohl ein class und ein struct Einschränkung zugleich:

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

Verbrauch:

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

Hinweis: dies speziell in der C # 5.0-Sprachspezifikation angegeben ist:

  

Wenn Typ-Parameter S abhängig vom Typ Parameter T dann:   [...] Es ist gültig für   S die Wert Typeinschränkung und T hat, um den Referenztyp hat   Zwang. Effektiv dies begrenzt T auf die Typen System.Object,   System.ValueType, System.Enum und jeder Interface-Typ.

Bearbeiten

Die Frage ist jetzt hervorragend von Julien Lebosquain beantwortet. Ich würde auch mit ignoreCase seine Antwort erweitern möchten, defaultValue und optionale Argumente, während das Hinzufügen TryParse und ParseOrDefault.

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 alte Verbesserungen auf Viveks Antwort durch die Kommentare mit und ‚neue‘ Entwicklungen:

  • Verwendung TEnum für Klarheit für Benutzer
  • fügen Sie weitere Schnittstelle-Einschränkungen für zusätzliche Einschränkung Prüfung
  • TryParse Griff ignoreCase mit dem vorhandenen Parameter (Eingeführt in VS2010 / .NET 4)
  • optional verwenden, um den generischen default Wert (in VS2005 / eingeführt. Net 2)
  • optionale Argumente (eingeführt in VS2010 / .NET 4) mit Standardwerten, für defaultValue und ignoreCase

ergibt:

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

Sie können einen statischen Konstruktor für die Klasse definieren, die prüfen wird, dass der Typ T eine Enumeration ist und eine Ausnahme auslösen, wenn dies nicht der Fall. Dies ist die Methode von Jeffery Richter in seinem Buch CLR via C # erwähnt.

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

Dann in der Parse-Methode können Sie nur Enum.Parse verwenden (typeof (T), Eingang, true) von String in die Enum zu konvertieren. Der letzte wahre Parameter ist für Fall der Eingabe ignoriert.

Es sollte auch, dass in Betracht gezogen werden, da die Freisetzung von C # 7.3 Enum Einschränkungen verwenden, wird out-of-the-box unterstützt ohne zusätzliche Prüfung und Sachen zu tun.

geht also vorwärts und da haben Sie die Sprachversion Ihres Projekts zu C # 7.3 den folgenden Code wird funktionieren völlig in Ordnung geändert:

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

Falls Sie nicht wissen, wie die Sprachversion auf C # 7.3 zu ändern, um die folgenden Screenshot sehen: eingeben Bild Beschreibung hier

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

Für Visual Studio zu erkennen, die neue Syntax Sie mindestens Version 15.7. Sie können feststellen, dass auch in Microsofts Release Notes erwähnt, finden Sie unter Visual Studio 2017 15,7 Release Notes . Dank @MohamedElshawaf für diese berechtigte Frage Hinweis.

Bitte beachten Sie zudem, dass in meinem Fall ReSharper 2.018,1 ab diesem EDIT Schreiben noch nicht C # 7.3 unterstützen. Mit ReSharper aktiviert es die Enum-Einschränkung als Fehler hebt mir zu sagen, Kann nicht verwenden ‚System.Array‘, ‚System.Delegate‘, ‚System.Enum‘, ‚System.ValueType‘, ‚Objekt‘ als Typ Parameterbedingung . ReSharper schlägt vor, als eine schnelle Lösung zu Entfernen 'Enum' Constraint vom Typ Paramter T der Methode

Wenn Sie jedoch ReSharper vorübergehend unter Werkzeuge ausschalten -> Optionen -> ReSharper Ultimative -> Allgemein Sie werden sehen, dass die Syntax völlig in Ordnung, dass Sie VS verwenden gegeben 15.7 oder höher und C # 7.3 oder höher.

I modifiziert, um die Probe durch dimarzionist. Diese Version wird nur mit Aufzählungen arbeiten und nicht zulassen, structs 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);
    }
}

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

ich habe spezifische Anforderung, wo ich benötigen Enum mit Text mit ENUM-Wert zugeordnet zu verwenden. Zum Beispiel, wenn ich Enum verwenden Fehlertyp angeben 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());
}

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

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

Zitat:

  

... Beiden Einschränkungen produzieren tatsächlich gültig IL und können auch von C # verbraucht werden, wenn in einer anderen Sprache geschrieben (man diese Einschränkungen in Managed C ++ oder in IL erklären kann).

Wer weiß,

Dies ist mein nehmen sie an. In Kombination 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

Die vorhandenen Antworten sind wahr, wie von C # <= 7,2. Allerdings gibt es eine C # Feature-Anfrage (gebunden an eine corefx Feature-Request), damit die folgenden;

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

Zum Zeitpunkt des Schreibens, das Merkmal "Im Gespräch" an den Language Development Meetings.

Bearbeiten

Wie pro Nawfal 's Info, dies in C # eingeführt wird 7.3 .

Ich möchte diese immer (man könnte gegebenenfalls ä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>();
}

Ich liebte Christopher Currens Lösung IL verwenden, aber für diejenigen, die nicht wollen, mit heikeler Angelegenheit befassen MSIL von einschließlich in ihren Build-Prozess I ähnliche Funktion in C # geschrieben.

Bitte beachten Sie aber, dass Sie nicht generische Einschränkung wie where T : Enum verwenden können, weil Enum spezielle Art ist. Deshalb muss ich überprüfen, ob gegeben generischer 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;
}

Ich habe Viveks Lösung in eine Utility-Klasse gekapselt, die Sie wiederverwenden können. Bitte beachten Sie, dass Sie noch Typ Einschränkungen definieren sollten „wo T: struct, IConvertible“. Auf dem Typ

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

Ich habe eine Erweiterungsmethode to get integer value from enum Blick auf, Methodenimplementierung

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

Dies ist die Nutzung

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

Wie in anderen Antworten erwähnt vor; während dies nicht in Quellcode ausgedrückt werden kann, kann es tatsächlich auf IL-Ebene durchgeführt werden. @Christopher Currens zeigt beantworten, wie die IL tun.

Mit Fody s Add-In ExtraConstraints.Fody gibt es eine sehr einfache Art und Weise, komplett mit Build-Werkzeuge, um dies zu erreichen. Fügen Sie einfach ihre nuget Pakete (Fody, ExtraConstraints.Fody) zu Ihrem Projekt und die Einschränkungen wie folgt hinzufügen (Auszug aus der Readme von ExtraConstraints):

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

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

und Fody die notwendige IL für die Einschränkung hinzufügen, anwesend zu sein. Beachten Sie auch die zusätzliche Funktion Delegierten Zwang:

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

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

In Bezug auf Aufzählungen, könnten Sie auch Kenntnis von dem hochinteressanten Enums.NET nehmen wollen .

Wenn es in Ordnung, danach Angießen zu verwenden, ich denke, Sie die System.Enum Basisklasse in Ihrer Methode verwenden können, wo immer notwendig. Sie müssen nur sorgfältig die Parameter des Typs ersetzen. So ist die Implementierung der Methode wäre wie:

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 verwenden mögen:

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

Nur der Vollständigkeit halber, wird die folgende Java-Lösung. Ich bin sicher, das gleiche könnte auch in C # erfolgen. Es wird vermieden, dass die Art überall in Code angeben -. Stattdessen Sie es in den Saiten legen Sie zu analysieren versuchen,

Das Problem ist, dass es keine Möglichkeit, die Aufzählung zu wissen, ist der String könnte passen -. So lautet die Antwort, dieses Problem zu lösen

Statt nur den String-Wert zu akzeptieren, akzeptiert einen String, der sowohl die Aufzählung und den Wert in der Form „enumeration.value“ hat. Arbeits Code unten ist - erfordert Java 1.8 oder höher. Dies würde auch den XML präziser als in Sie so etwas wie Farbe sehen würde = „Color.red“ statt nur color = „red“.

Sie würden die acceptEnumeratedValue () -Methode mit einem String rufen Sie den Enum-Namen Punktwert Namen enthält.

Die Methode gibt den formalen Aufzählungswert.

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;


public class EnumFromString {

    enum NumberEnum {One, Two, Three};
    enum LetterEnum {A, B, C};


    Map<String, Function<String, ? extends Enum>> enumsByName = new HashMap<>();

    public static void main(String[] args) {
        EnumFromString efs = new EnumFromString();

        System.out.print("\nFirst string is NumberEnum.Two - enum is " + efs.acceptEnumeratedValue("NumberEnum.Two").name());
        System.out.print("\nSecond string is LetterEnum.B - enum is " + efs.acceptEnumeratedValue("LetterEnum.B").name());

    }

    public EnumFromString() {
        enumsByName.put("NumberEnum", s -> {return NumberEnum.valueOf(s);});
        enumsByName.put("LetterEnum", s -> {return LetterEnum.valueOf(s);});
    }

    public Enum acceptEnumeratedValue(String enumDotValue) {

        int pos = enumDotValue.indexOf(".");

        String enumName = enumDotValue.substring(0, pos);
        String value = enumDotValue.substring(pos + 1);

        Enum enumeratedValue = enumsByName.get(enumName).apply(value);

        return enumeratedValue;
    }


}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top