Konvertieren Sie eine Zeichenfolge in eine Aufzählung in C #

894

Wie kann eine Zeichenfolge in C # am besten in einen Aufzählungswert konvertiert werden?

Ich habe ein HTML-Auswahl-Tag, das die Werte einer Aufzählung enthält. Wenn die Seite veröffentlicht wird, möchte ich den Wert (der in Form einer Zeichenfolge vorliegen wird) aufnehmen und in den Aufzählungswert konvertieren.

In einer idealen Welt könnte ich so etwas tun:

StatusEnum MyStatus = StatusEnum.Parse("Active");

aber das ist kein gültiger Code.

Ben Mills
quelle

Antworten:

1508

In .NET Core und .NET> 4 gibt es eine generische Analysemethode :

Enum.TryParse("Active", out StatusEnum myStatus);

Dies schließt auch die neuen Inline- outVariablen von C # 7 ein , also die Try-Parse, die Konvertierung in den expliziten Aufzählungstyp und die Initialisierung + Auffüllung der myStatusVariablen.

Wenn Sie Zugriff auf C # 7 und das neueste .NET haben, ist dies der beste Weg.

Ursprüngliche Antwort

In .NET ist es ziemlich hässlich (bis 4 oder höher):

StatusEnum MyStatus = (StatusEnum) Enum.Parse(typeof(StatusEnum), "Active", true);

Ich neige dazu, dies zu vereinfachen mit:

public static T ParseEnum<T>(string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

Dann kann ich tun:

StatusEnum MyStatus = EnumUtil.ParseEnum<StatusEnum>("Active");

Eine in den Kommentaren vorgeschlagene Option ist das Hinzufügen einer Erweiterung, die einfach genug ist:

public static T ToEnum<T>(this string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

StatusEnum MyStatus = "Active".ToEnum<StatusEnum>();

Schließlich möchten Sie möglicherweise eine Standardaufzählung verwenden, wenn die Zeichenfolge nicht analysiert werden kann:

public static T ToEnum<T>(this string value, T defaultValue) 
{
    if (string.IsNullOrEmpty(value))
    {
        return defaultValue;
    }

    T result;
    return Enum.TryParse<T>(value, true, out result) ? result : defaultValue;
}

Was dies zum Aufruf macht:

StatusEnum MyStatus = "Active".ToEnum(StatusEnum.None);

Ich würde jedoch vorsichtig sein, wenn ich eine Erweiterungsmethode wie diese hinzufüge, stringda (ohne Namespace-Kontrolle) in allen Fällen stringangezeigt wird, ob sie eine Aufzählung enthalten oder nicht (dies 1234.ToString().ToEnum(StatusEnum.None)wäre gültig, aber unsinnig). Es ist oft am besten, die Kernklassen von Microsoft nicht mit zusätzlichen Methoden zu überladen, die nur in ganz bestimmten Kontexten gelten, es sei denn, Ihr gesamtes Entwicklungsteam hat ein sehr gutes Verständnis für die Funktionsweise dieser Erweiterungen.

Keith
quelle
17
Wenn Leistung wichtig ist (was immer der Fall
Nash
28
@avinashr hat Recht mit der Antwort von @ McKenzieG1, aber es ist nicht IMMER wichtig. Zum Beispiel wäre es eine sinnlose Mikrooptimierung, sich Gedanken über das Parsen von Aufzählungen zu machen, wenn Sie für jede Analyse einen DB-Aufruf durchführen würden.
Keith
4
@HM Ich denke nicht, dass eine Erweiterung hier angemessen ist - es ist ein Sonderfall und eine Erweiterung würde für jede Zeichenfolge gelten. Wenn Sie es wirklich tun wollten, wäre es eine triviale Änderung.
Keith
7
Wie wäre es mit Enum.TryParse?
Elaine
15
Sehr schön. In Ihrem letzten Beispiel benötigen Sie eine where T: struct.
Bbrik
331

Verwendung Enum.TryParse<T>(String, T)(≥ .NET 4.0):

StatusEnum myStatus;
Enum.TryParse("Active", out myStatus);

Mit dem Inlining des Parametertyps von C # 7.0 kann dies noch weiter vereinfacht werden :

Enum.TryParse("Active", out StatusEnum myStatus);
Erwin Mayer
quelle
45
Fügen Sie den mittleren booleschen Parameter für die Groß- und Kleinschreibung hinzu, und dies ist bei weitem die sicherste und eleganteste Lösung.
DanM7
18
Komm schon, wie viele von euch haben diese ausgewählte Antwort aus dem Jahr 2008 implementiert, um nur nach unten zu scrollen und herauszufinden, dass dies die bessere (moderne) Antwort ist.
TEK
@TEK Ich bevorzuge eigentlich die Antwort von 2008.
Zero3
Ich verstehe es nicht ParseWirft erklärende Ausnahmen für das, was bei der Konvertierung schief gelaufen ist (Wert war null, leer oder keine entsprechende Enum-Konstante), was viel besser ist als TryParseder boolesche Rückgabewert (der den konkreten Fehler unterdrückt)
yair
2
Enum.TryParse <T> (String, T) ist beim Parsen von Integer-Strings fehlerhaft. Zum Beispiel wird dieser Code eine unsinnige Zeichenfolge erfolgreich als unsinnige Aufzählung analysieren: var result = Enum.TryParse<System.DayOfWeek>("55", out var parsedEnum);
Mass Dot Net
196

Beachten Sie, dass die Leistung von Enum.Parse()schrecklich ist, da sie über Reflexion implementiert wird. (Dasselbe gilt für das Enum.ToString, was in die andere Richtung geht.)

Wenn Sie Zeichenfolgen in leistungsabhängigem Code in Enums konvertieren müssen, erstellen Sie am besten eine Dictionary<String,YourEnum>beim Start und verwenden Sie diese für Ihre Konvertierungen.

McKenzieG1
quelle
10
Ich habe 3 ms gemessen, um eine Zeichenfolge beim ersten Durchlauf auf einem Desktop-Computer in eine Aufzählung umzuwandeln. (Nur um das Ausmaß der Schrecklichkeit zu veranschaulichen).
Matthieu Charbonnier
12
Wow 3ms ist Größenordnungen schrecklich
John Stock
1
können Sie ein Codebeispiel , um dieses hinzuzufügen, so dass wir eine Idee bekommen , wie zu ersetzen und Verwendung
Transformator ,
Wenn Ihre App von 1 Million Menschen verwendet wird => summiert sich dies auf 50 Stunden Menschenleben, die Sie verbrauchen :) Auf einer einzigen Seite. : P
Cătălin Rădoi
95

Sie suchen nach Enum.Parse .

SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue");
David Whitney
quelle
31

Sie können jetzt Erweiterungsmethoden verwenden :

public static T ToEnum<T>(this string value, bool ignoreCase = true)
{
    return (T) Enum.Parse(typeof (T), value, ignoreCase);
}

Und Sie können sie mit dem folgenden Code aufrufen (hier FilterTypeist ein Aufzählungstyp):

FilterType filterType = type.ToEnum<FilterType>();
Foyzul Karim
quelle
1
Ich habe dies aktualisiert, um den Wert als Objekt zu verwenden und ihn in eine Zeichenfolge innerhalb dieser Methode umzuwandeln. Auf diese Weise kann ich einen int-Wert .ToEnum anstelle von nur Zeichenfolgen verwenden.
RealSollyM
2
@SollyM Ich würde sagen, das ist eine schreckliche Idee, denn dann gilt diese Erweiterungsmethode für alle Objekttypen. Zwei Erweiterungsmethoden, eine für string und eine für int, wären meiner Meinung nach sauberer und viel sicherer.
Svish
@Svish, das stimmt. Der einzige Grund, warum ich dies getan habe, ist, dass unser Code nur intern verwendet wird und ich vermeiden wollte, 2 Erweiterungen zu schreiben. Und da wir nur mit string oder int nach Enum konvertieren, habe ich es sonst nicht als Problem angesehen.
RealSollyM
3
@SollyM Intern oder nicht, ich bin immer noch derjenige, der meinen Code verwaltet und verwendet: PI wäre verärgert, wenn ich in jedem Intellisense-Menü eine ToEnum aufrufen würde, und wie Sie sagen, da Sie nur eine Zeichenfolge in eine Aufzählung konvertieren oder int, Sie können ziemlich sicher sein, dass Sie nur diese beiden Methoden benötigen. Und zwei Methoden sind nicht viel mehr als eine, besonders wenn sie so klein und vom Typ Dienstprogramm sind: P
Svish
20
object Enum.Parse(System.Type enumType, string value, bool ignoreCase);

Wenn Sie also eine Aufzählung mit dem Namen Stimmung hätten, würde dies folgendermaßen aussehen:

   enum Mood
   {
      Angry,
      Happy,
      Sad
   } 

   // ...
   Mood m = (Mood) Enum.Parse(typeof(Mood), "Happy", true);
   Console.WriteLine("My mood is: {0}", m.ToString());
brendan
quelle
18

IN ACHT NEHMEN:

enum Example
{
    One = 1,
    Two = 2,
    Three = 3
}

Enum.(Try)Parse() akzeptiert mehrere durch Kommas getrennte Argumente und kombiniert sie mit binären 'oder'| . Sie können dies nicht deaktivieren und meiner Meinung nach wollen Sie es fast nie.

var x = Enum.Parse("One,Two"); // x is now Three

Auch wenn Threenicht definiert wurde, xwürde immer noch int Wert erhalten 3. Das ist noch schlimmer: Enum.Parse () kann Ihnen einen Wert geben, der nicht einmal für die Aufzählung definiert ist!

Ich möchte nicht die Konsequenzen von Benutzern erfahren, die bereitwillig oder unfreiwillig dieses Verhalten auslösen.

Darüber hinaus ist die Leistung, wie von anderen erwähnt, für große Aufzählungen weniger als ideal, nämlich linear in der Anzahl der möglichen Werte.

Ich schlage folgendes vor:

    public static bool TryParse<T>(string value, out T result)
        where T : struct
    {
        var cacheKey = "Enum_" + typeof(T).FullName;

        // [Use MemoryCache to retrieve or create&store a dictionary for this enum, permanently or temporarily.
        // [Implementation off-topic.]
        var enumDictionary = CacheHelper.GetCacheItem(cacheKey, CreateEnumDictionary<T>, EnumCacheExpiration);

        return enumDictionary.TryGetValue(value.Trim(), out result);
    }

    private static Dictionary<string, T> CreateEnumDictionary<T>()
    {
        return Enum.GetValues(typeof(T))
            .Cast<T>()
            .ToDictionary(value => value.ToString(), value => value, StringComparer.OrdinalIgnoreCase);
    }
Timo
quelle
4
In der Tat ist dies sehr nützlich, um das zu wissen Enum.(Try)Parse accepts multiple, comma-separated arguments, and combines them with binary 'or'. Dies bedeutet, dass Sie Ihre Enum-Werte als Zweierpotenzen einrichten können und auf sehr einfache Weise mehrere boolesche Flags analysieren können, z. "UseSSL, NoRetries, Sync". Genau dafür wurde es wahrscheinlich entwickelt.
PCDEV
16

Enum.Parse ist dein Freund:

StatusEnum MyStatus = (StatusEnum)Enum.Parse(typeof(StatusEnum), "Active");
tags2k
quelle
13

Sie können die akzeptierte Antwort um einen Standardwert erweitern, um Ausnahmen zu vermeiden:

public static T ParseEnum<T>(string value, T defaultValue) where T : struct
{
    try
    {
        T enumValue;
        if (!Enum.TryParse(value, true, out enumValue))
        {
            return defaultValue;
        }
        return enumValue;
    }
    catch (Exception)
    {
        return defaultValue;
    }
}

Dann nennst du es wie:

StatusEnum MyStatus = EnumUtil.ParseEnum("Active", StatusEnum.None);

Wenn der Standardwert keine Aufzählung ist, schlägt Enum.TryParse fehl und löst eine Ausnahme aus, die abgefangen wird.

Nach Jahren der Verwendung dieser Funktion in unserem Code an vielen Stellen ist es vielleicht gut, die Information hinzuzufügen, dass dieser Vorgang die Leistung kostet!

Nelly
quelle
Ich mag keine Standardwerte. Dies kann zu unvorhersehbaren Ergebnissen führen.
Daniël Tulp
5
Wann wird dies jemals eine Ausnahme auslösen?
andleer
@andleer, wenn der Aufzählungswert nicht zum gleichen Aufzählungstyp wie der Standardwert passt
Nelly
@Nelly Alter Code hier, aber der defaultValueRückgabetyp und der Methodenrückgabetyp sind beide vom Typ T. Wenn die Typen unterschiedlich sind, wird ein Fehler bei der Kompilierung angezeigt: "Konvertierung von 'ConsoleApp1.Size' in 'ConsoleApp1.Color' nicht möglich" oder was auch immer Ihre Typen sind.
andleer
@andleer, es tut mir leid, dass meine letzte Antwort an Sie nicht korrekt war. Es ist möglich, dass diese Methode eine Syste.ArgumentException auslöst, falls jemand diese Funktion mit einem Standardwert aufruft, der nicht vom Typ enum ist. Mit c # 7.0 konnte ich keine where-Klausel von T: Enum erstellen. Deshalb habe ich diese Möglichkeit mit einem Versuch gefangen.
Nelly
8

Wir konnten keine vollkommen gültigen Eingaben annehmen und haben uns für diese Variante der Antwort von @ Keith entschieden:

public static TEnum ParseEnum<TEnum>(string value) where TEnum : struct
{
    TEnum tmp; 
    if (!Enum.TryParse<TEnum>(value, true, out tmp))
    {
        tmp = new TEnum();
    }
    return tmp;
}
Spalt
quelle
7
// str.ToEnum<EnumType>()
T static ToEnum<T>(this string str) 
{ 
    return (T) Enum.Parse(typeof(T), str);
}
Mark Cidade
quelle
5

Analysiert den String ohne try / catch und ohne TryParse () -Methode aus .NET 4.5 in TEnum

/// <summary>
/// Parses string to TEnum without try/catch and .NET 4.5 TryParse()
/// </summary>
public static bool TryParseToEnum<TEnum>(string probablyEnumAsString_, out TEnum enumValue_) where TEnum : struct
{
    enumValue_ = (TEnum)Enum.GetValues(typeof(TEnum)).GetValue(0);
    if(!Enum.IsDefined(typeof(TEnum), probablyEnumAsString_))
        return false;

    enumValue_ = (TEnum) Enum.Parse(typeof(TEnum), probablyEnumAsString_);
    return true;
}
jite.gs
quelle
1
Ob eine Beschreibung erforderlich ist, wenn der Code bereits eine Beschreibung enthält? Ok, ich habe das gemacht :)
jite.gs
3

Super einfacher Code mit TryParse:

var value = "Active";

StatusEnum status;
if (!Enum.TryParse<StatusEnum>(value, out status))
    status = StatusEnum.Unknown;
Brian Rice
quelle
2

Ich mag die Lösung der Erweiterungsmethode.

namespace System
{
    public static class StringExtensions
    {

        public static bool TryParseAsEnum<T>(this string value, out T output) where T : struct
        {
            T result;

            var isEnum = Enum.TryParse(value, out result);

            output = isEnum ? result : default(T);

            return isEnum;
        }
    }
}

Hier unten meine Implementierung mit Tests.

using static Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
using static System.Console;

private enum Countries
    {
        NorthAmerica,
        Europe,
        Rusia,
        Brasil,
        China,
        Asia,
        Australia
    }

   [TestMethod]
        public void StringExtensions_On_TryParseAsEnum()
        {
            var countryName = "Rusia";

            Countries country;
            var isCountry = countryName.TryParseAsEnum(out country);

            WriteLine(country);

            IsTrue(isCountry);
            AreEqual(Countries.Rusia, country);

            countryName = "Don't exist";

            isCountry = countryName.TryParseAsEnum(out country);

            WriteLine(country);

            IsFalse(isCountry);
            AreEqual(Countries.NorthAmerica, country); // the 1rst one in the enumeration
        }
Alhpe
quelle
1
public static T ParseEnum<T>(string value)            //function declaration  
{
    return (T) Enum.Parse(typeof(T), value);
}

Importance imp = EnumUtil.ParseEnum<Importance>("Active");   //function call

==================== Ein vollständiges Programm ====================

using System;

class Program
{
    enum PetType
    {
    None,
    Cat = 1,
    Dog = 2
    }

    static void Main()
    {

    // Possible user input:
    string value = "Dog";

    // Try to convert the string to an enum:
    PetType pet = (PetType)Enum.Parse(typeof(PetType), value);

    // See if the conversion succeeded:
    if (pet == PetType.Dog)
    {
        Console.WriteLine("Equals dog.");
    }
    }
}
-------------
Output

Equals dog.
Rae Lee
quelle
1

Ich habe class verwendet (stark typisierte Version von Enum mit Analyse und Leistungsverbesserungen). Ich habe es auf GitHub gefunden und es sollte auch für .NET 3.5 funktionieren. Es hat etwas Speicheraufwand, da es ein Wörterbuch puffert.

StatusEnum MyStatus = Enum<StatusEnum>.Parse("Active");

Der Blogpost lautet Enums - Bessere Syntax, verbesserte Leistung und TryParse in NET 3.5 .

Und Code: https://github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/System/EnumT.cs

Patrik Lindström
quelle
1

Für die Leistung könnte dies helfen:

    private static Dictionary<Type, Dictionary<string, object>> dicEnum = new Dictionary<Type, Dictionary<string, object>>();
    public static T ToEnum<T>(this string value, T defaultValue)
    {
        var t = typeof(T);
        Dictionary<string, object> dic;
        if (!dicEnum.ContainsKey(t))
        {
            dic = new Dictionary<string, object>();
            dicEnum.Add(t, dic);
            foreach (var en in Enum.GetValues(t))
                dic.Add(en.ToString(), en);
        }
        else
            dic = dicEnum[t];
        if (!dic.ContainsKey(value))
            return defaultValue;
        else
            return (T)dic[value];
    }
Koray
quelle
1

Ich fand, dass hier der Fall mit Enum-Werten, die EnumMember-Wert haben, nicht berücksichtigt wurde. Auf geht's:

using System.Runtime.Serialization;

public static TEnum ToEnum<TEnum>(this string value, TEnum defaultValue) where TEnum : struct
{
    if (string.IsNullOrEmpty(value))
    {
        return defaultValue;
    }

    TEnum result;
    var enumType = typeof(TEnum);
    foreach (var enumName in Enum.GetNames(enumType))
    {
        var fieldInfo = enumType.GetField(enumName);
        var enumMemberAttribute = ((EnumMemberAttribute[]) fieldInfo.GetCustomAttributes(typeof(EnumMemberAttribute), true)).FirstOrDefault();
        if (enumMemberAttribute?.Value == value)
        {
            return Enum.TryParse(enumName, true, out result) ? result : defaultValue;
        }
    }

    return Enum.TryParse(value, true, out result) ? result : defaultValue;
}

Und ein Beispiel für diese Aufzählung:

public enum OracleInstanceStatus
{
    Unknown = -1,
    Started = 1,
    Mounted = 2,
    Open = 3,
    [EnumMember(Value = "OPEN MIGRATE")]
    OpenMigrate = 4
}
isxaker
quelle
1

Sie müssen Enum.Parse verwenden, um den Objektwert von Enum abzurufen. Danach müssen Sie den Objektwert in einen bestimmten Aufzählungswert ändern. Das Umwandeln in einen Aufzählungswert kann mithilfe von Convert.ChangeType erfolgen. Bitte schauen Sie sich das folgende Code-Snippet an

public T ConvertStringValueToEnum<T>(string valueToParse){
    return Convert.ChangeType(Enum.Parse(typeof(T), valueToParse, true), typeof(T));
}
Bartosz Gawron
quelle
1

Probieren Sie dieses Beispiel aus:

 public static T GetEnum<T>(string model)
    {
        var newModel = GetStringForEnum(model);

        if (!Enum.IsDefined(typeof(T), newModel))
        {
            return (T)Enum.Parse(typeof(T), "None", true);
        }

        return (T)Enum.Parse(typeof(T), newModel.Result, true);
    }

    private static Task<string> GetStringForEnum(string model)
    {
        return Task.Run(() =>
        {
            Regex rgx = new Regex("[^a-zA-Z0-9 -]");
            var nonAlphanumericData = rgx.Matches(model);
            if (nonAlphanumericData.Count < 1)
            {
                return model;
            }
            foreach (var item in nonAlphanumericData)
            {
                model = model.Replace((string)item, "");
            }
            return model;
        });
    }

In diesem Beispiel können Sie jede Zeichenfolge senden und Ihre festlegen Enum. Wenn Sie EnumDaten hatten, die Sie wollten, geben Sie diese als Ihren EnumTyp zurück.

AmirReza-Farahlagha
quelle
1
Sie überschreiben newModeljede Zeile. Wenn sie Bindestriche enthält, wird sie nicht ersetzt. Außerdem müssen Sie nicht überprüfen, ob die Zeichenfolge etwas enthält. Sie können sie Replacetrotzdem aufrufen :var newModel = model.Replace("-", "").Replace(" ", "");
Lars Kristensen
@LarsKristensen Ja, wir können eine Methode erstellen, die nichtalphanumerische Zeichen entfernt.
AmirReza-Farahlagha
1

Ich bin mir nicht sicher, wann dies hinzugefügt wurde, aber in der Enum-Klasse gibt es jetzt eine

Parse<TEnum>(stringValue)

Wird so mit dem fraglichen Beispiel verwendet:

var MyStatus = Enum.Parse<StatusEnum >("Active")

oder Gehäuse ignorieren durch:

var MyStatus = Enum.Parse<StatusEnum >("active", true)

Hier sind die dekompilierten Methoden, die hier verwendet werden:

    [NullableContext(0)]
    public static TEnum Parse<TEnum>([Nullable(1)] string value) where TEnum : struct
    {
      return Enum.Parse<TEnum>(value, false);
    }

    [NullableContext(0)]
    public static TEnum Parse<TEnum>([Nullable(1)] string value, bool ignoreCase) where TEnum : struct
    {
      TEnum result;
      Enum.TryParse<TEnum>(value, ignoreCase, true, out result);
      return result;
    }
JCisar
quelle
0
        <Extension()>
    Public Function ToEnum(Of TEnum)(ByVal value As String, ByVal defaultValue As TEnum) As TEnum
        If String.IsNullOrEmpty(value) Then
            Return defaultValue
        End If

        Return [Enum].Parse(GetType(TEnum), value, True)
    End Function
AHMED RABEE
quelle
0
public TEnum ToEnum<TEnum>(this string value, TEnum defaultValue){
if (string.IsNullOrEmpty(value))
    return defaultValue;

return Enum.Parse(typeof(TEnum), value, true);}
AHMED RABEE
quelle
0

Wenn sich der Name der Eigenschaft von dem unterscheidet, den Sie nennen möchten (dh Sprachunterschiede), können Sie Folgendes tun:

MyType.cs

using System;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

[JsonConverter(typeof(StringEnumConverter))]
public enum MyType
{
    [EnumMember(Value = "person")]
    Person,
    [EnumMember(Value = "annan_deltagare")]
    OtherPerson,
    [EnumMember(Value = "regel")]
    Rule,
}

EnumExtensions.cs

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

public static class EnumExtensions
{
    public static TEnum ToEnum<TEnum>(this string value) where TEnum : Enum
    {
        var jsonString = $"'{value.ToLower()}'";
        return JsonConvert.DeserializeObject<TEnum>(jsonString, new StringEnumConverter());
    }

    public static bool EqualsTo<TEnum>(this string strA, TEnum enumB) where TEnum : Enum
    {
        TEnum enumA;
        try
        {
            enumA = strA.ToEnum<TEnum>();
        }
        catch
        {
            return false;
        }
        return enumA.Equals(enumB);
    }
}

Program.cs

public class Program
{
    static public void Main(String[] args) 
    { 
        var myString = "annan_deltagare";
        var myType = myString.ToEnum<MyType>();
        var isEqual = myString.EqualsTo(MyType.OtherPerson);
        //Output: true
    }     
}
Joel Wiklund
quelle