Konwertuj ciąg znaków na enum w C#

Jaki jest najlepszy sposób na konwersję ciągu znaków na wartość wyliczenia w C#?

Mam znacznik HTML select zawierający wartości z wyliczenia. Kiedy strona jest publikowana, chcę podnieść wartość (która będzie w postaci łańcucha znaków) i przekonwertować ją na odpowiednią wartość wyliczenia.

W idealnym świecie mógłbym zrobić coś takiego:

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

Ale to nie jest prawidłowy kod.

Author: wp78de, 2008-08-19

25 answers

W. NET Core i. NET Framework ≥4.0 istnieje ogólna metoda parse :

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

Obejmuje to również nowe zmienne inline out W C#7, więc wykonuje to try-parse, konwersję do jawnego typu enum i inicjalizuje+wypełniając zmienną myStatus.

Jeśli masz dostęp do C # 7 i najnowszego. NET to jest najlepszy sposób.

Oryginalna Odpowiedź

W. NET jest raczej brzydko (do 4 lub wyżej):

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

Staram się uprościć to z:

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

Wtedy mogę zrobić:

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

Jedną z opcji sugerowanych w komentarzach jest dodanie rozszerzenia, co jest dość proste:

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

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

Wreszcie, możesz chcieć użyć domyślnego enum, jeśli łańcuch nie może być parsowany:

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

Co sprawia, że jest to wezwanie:

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

Jednak byłbym ostrożny dodając metodę rozszerzenia jak ta do string jako (bez kontroli przestrzeni nazw) będzie ona wyświetlana na wszystkich instancjach string czy posiadają enum czy nie (so 1234.ToString().ToEnum(StatusEnum.None) byłoby słuszne, ale bezsensowne) . Często najlepiej jest unikać zaśmiecania podstawowych klas Microsoftu dodatkowymi metodami, które mają zastosowanie tylko w bardzo specyficznych kontekstach, chyba że cały zespół programistów ma bardzo dobre zrozumienie tego, co robią te rozszerzenia.

 1669
Author: Keith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-14 19:39:07

Użycie Enum.TryParse<T>(String, T) (≥ . NET 4.0):

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

Może być jeszcze bardziej uproszczone z C# 7.0 ' s typ parametru inlining :

Enum.TryParse("Active", out StatusEnum myStatus);
 350
Author: Erwin Mayer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-14 16:20:56

Zauważ, że wydajność Enum.Parse() jest okropna, ponieważ jest zaimplementowana poprzez odbicie. (To samo dotyczy Enum.ToString, które idzie w drugą stronę.)

Jeśli chcesz przekonwertować ciągi znaków na liczby w kodzie wrażliwym na wydajność, najlepiej utworzyć Dictionary<String,YourEnum> przy starcie i użyć go do konwersji.

 213
Author: McKenzieG1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-26 01:19:27

Szukasz Enum.Parse .

SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue");
 103
Author: DavidWhitney,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-30 12:37:12

Możesz użyć metody rozszerzenia Teraz:

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

I możesz je wywołać za pomocą poniższego kodu (tutaj FilterType jest typem enum):

FilterType filterType = type.ToEnum<FilterType>();
 32
Author: Foyzul Karim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-30 12:40:29

Uwaga:

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

Enum.(Try)Parse() przyjmuje wiele argumentów oddzielonych przecinkami i łączy je z binarnym 'lub' |. Nie możesz tego wyłączyć i moim zdaniem prawie nigdy tego nie chcesz.

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

Nawet jeśli Three nie został zdefiniowany, x nadal otrzymałby wartość int 3. To jeszcze gorzej: Enum.Parse() może dać wartość, która nie jest nawet zdefiniowana dla enum!

Nie chciałbym doświadczać konsekwencji użytkowników, chętnie lub niechętnie, wywołanie takiego zachowania.

Dodatkowo, jak wspomniano przez innych, wydajność jest mniejsza niż idealna dla dużych enum, a mianowicie liniowa w liczbie możliwych wartości.

Proponuję:

    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);
    }
 23
Author: Timo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-22 08:21:54
object Enum.Parse(System.Type enumType, string value, bool ignoreCase);

Więc gdybyś miał enum o nazwie mood to wyglądałoby to tak:

   enum Mood
   {
      Angry,
      Happy,
      Sad
   } 

   // ...
   Mood m = (Mood) Enum.Parse(typeof(Mood), "Happy", true);
   Console.WriteLine("My mood is: {0}", m.ToString());
 21
Author: brendan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-19 12:58:57

Enum.Parse jest twoim przyjacielem:

StatusEnum MyStatus = (StatusEnum)Enum.Parse(typeof(StatusEnum), "Active");
 16
Author: tags2k,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-30 12:37:32

Możesz rozszerzyć akceptowaną odpowiedź o wartość domyślną, aby uniknąć WYJĄTKÓW:

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

Wtedy nazywacie to tak:

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

Jeśli wartością domyślną nie jest enum, to Enum.TryParse nie powiedzie się i rzuci wyjątek, który zostanie złapany.

Po latach używania tej funkcji w naszym kodzie w wielu miejscach może warto dodać informację, że ta operacja kosztuje wydajność!

 13
Author: Nelly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-02 09:45:53

Nie mogliśmy założyć idealnie poprawnego wejścia, i poszliśmy z tą odmianą odpowiedzi @ Keith:

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;
}
 8
Author: gap,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-30 15:07:10
// str.ToEnum<EnumType>()
T static ToEnum<T>(this string str) 
{ 
    return (T) Enum.Parse(typeof(T), str);
}
 7
Author: Mark Cidade,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-05 15:06:47

Parsuje string do TEnum bez metody try/catch i bez metody TryParse () z. NET 4.5

/// <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;
}
 5
Author: jite.gs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-30 12:46:11

Nie wiem kiedy to zostało dodane, ale na klasie Enum jest teraz

Parse<TEnum>(stringValue)

Użyte w ten sposób z przykładem w pytaniu:

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

Lub ignorując obudowę przez:

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

Oto metody dekompilacji, których używa:

    [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;
    }
 4
Author: JCisar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-27 18:39:51

Super prosty kod za pomocą TryParse:

var value = "Active";

StatusEnum status;
if (!Enum.TryParse<StatusEnum>(value, out status))
    status = StatusEnum.Unknown;
 3
Author: Brian Rice,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-25 02:30:35

Podoba mi się rozwiązanie metody rozszerzenia..

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

Poniżej moja implementacja z testami.

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
        }
 2
Author: alhpe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-01 21:57:29
public static T ParseEnum<T>(string value)            //function declaration  
{
    return (T) Enum.Parse(typeof(T), value);
}

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

====================Kompletny Program====================

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.
 1
Author: Rae Lee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-18 05:28:07

Użyłem class (mocno wpisanej wersji Enum z parsowaniem i ulepszeniami wydajności). Znalazłem go na Githubie i powinien działać również dla. NET 3.5. Ma trochę pamięci, ponieważ buforuje słownik.

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

Blogpost jest Enums-lepsza składnia, lepsza wydajność i Tryparowanie w sieci 3.5.

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

 1
Author: Patrik Lindström,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-30 16:33:43

Dla wydajności może to pomóc:

    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];
    }
 1
Author: Koray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-22 14:10:58

Odkryłem, że tutaj przypadek z wartościami enum, które mają wartość EnumMember nie został uwzględniony. No to zaczynamy:

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

I przykład tego enum:

public enum OracleInstanceStatus
{
    Unknown = -1,
    Started = 1,
    Mounted = 2,
    Open = 3,
    [EnumMember(Value = "OPEN MIGRATE")]
    OpenMigrate = 4
}
 1
Author: isxaker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-04 16:40:31

Musisz użyć Enum.Parse, aby uzyskać wartość obiektu z Enum, następnie musisz zmienić wartość obiektu na określoną wartość enum. Odlewanie do wartości enum można wykonać za pomocą konwersji.Typ zmiany. Proszę spojrzeć na poniższy fragment kodu

public T ConvertStringValueToEnum<T>(string valueToParse){
    return Convert.ChangeType(Enum.Parse(typeof(T), valueToParse, true), typeof(T));
}
 1
Author: Bartosz Gawron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-12 12:11:01

Spróbuj tej próbki:

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

W tej próbce możesz wysłać każdy ciąg i ustawić Enum. Jeśli twój Enum miał dane, które chciałeś, zwróć je jako swój typ Enum.

 1
Author: AmirReza-Farahlagha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-17 14:59:18

Jeśli nazwa właściwości różni się od tego, co chcesz nazwać (np. różnice językowe), możesz zrobić tak:

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
    }     
}
 1
Author: Joel Wiklund,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-24 13:28:38
        <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
 0
Author: AHMED RABEE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-22 07:15:54
public TEnum ToEnum<TEnum>(this string value, TEnum defaultValue){
if (string.IsNullOrEmpty(value))
    return defaultValue;

return Enum.Parse(typeof(TEnum), value, true);}
 0
Author: AHMED RABEE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-22 07:19:51

Jeśli chcesz użyć wartości domyślnej, gdy jest null lub empty (np. podczas pobierania z pliku konfiguracyjnego i wartość nie istnieje) i rzucać wyjątek, gdy łańcuch lub liczba nie pasują do żadnej z wartości enum. Uwaga na zastrzeżenie w odpowiedzi Timo ( https://stackoverflow.com/a/34267134/2454604).

    public static T ParseEnum<T>(this string s, T defaultValue, bool ignoreCase = false) 
        where T : struct, IComparable, IConvertible, IFormattable//If C# >=7.3: struct, System.Enum 
    {
        if ((s?.Length ?? 0) == 0)
        {
            return defaultValue;
        }

        var valid = Enum.TryParse<T>(s, ignoreCase, out T res);

        if (!valid || !Enum.IsDefined(typeof(T), res))
        {
            throw new InvalidOperationException(
                $"'{s}' is not a valid value of enum '{typeof(T).FullName}'!");
        }
        return res;
    }
 0
Author: MBWise,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-12 14:02:05