C #에서 문자열을 열거 형으로 변환


894

C #에서 문자열을 열거 형 값으로 변환하는 가장 좋은 방법은 무엇입니까?

열거 형 값을 포함하는 HTML select 태그가 있습니다. 페이지가 게시되면 값 (문자열 형식)을 가져 와서 열거 형 값으로 변환하고 싶습니다.

이상적인 세상에서는 다음과 같이 할 수 있습니다.

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

그러나 그것은 유효한 코드가 아닙니다.

답변:


1508

.NET Core 및 .NET> 4 에는 일반적인 구문 분석 방법이 있습니다 .

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

여기에는 C # 7의 새로운 인라인 out변수 도 포함 되므로 try-parse를 수행하고 명시 적 열거 형 유형으로 변환하고 myStatus변수를 초기화하고 채 웁니다 .

C # 7 및 최신 .NET에 액세스 할 수있는 것이 가장 좋습니다.

원래 답변

.NET에서는 다소 추악합니다 (4 이상).

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

나는 이것을 다음과 같이 단순화하는 경향이있다 :

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

그런 다음 할 수 있습니다 :

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

의견에서 제안 된 옵션 중 하나는 확장을 추가하는 것입니다.

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

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

마지막으로 문자열을 구문 분석 할 수없는 경우 사용할 기본 열거 형을 가질 수 있습니다.

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

이것은 이것을 전화로 만듭니다 :

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

그러나 string(네임 스페이스 제어없이) 이와 같은 확장 메소드를 추가 string하면 열거 형을 보유하는지 여부 에 관계없이 모든 인스턴스에 표시됩니다 ( 1234.ToString().ToEnum(StatusEnum.None)유효하지만 무의미합니다). 전체 개발 팀이 확장 기능에 대해 잘 이해하지 않는 한 매우 구체적인 상황에만 적용되는 추가 메서드를 사용하여 Microsoft의 핵심 클래스를 어수선하게 만드는 것이 가장 좋습니다.


17
성능이 중요한 경우 (항상 그러하다) 아래의 Mckenzieg1에서 제공 한 chk 답변 : stackoverflow.com/questions/16100/…
Nash

28
@avinashr은 @ McKenzieG1의 답변에 관한 것이지만 항상 중요하지는 않습니다. 예를 들어 각 구문 분석에 대해 DB 호출을 수행하는 경우 열거 구문 분석에 대해 걱정하는 것은 무의미한 마이크로 최적화입니다.
Keith

4
@ HM 확장이 여기에 적절하지 않다고 생각합니다. 약간의 특별한 경우이며 확장은 모든 문자열에 적용됩니다 . 당신이 정말로 그것을 원한다면 그것은 사소한 변화 일 것입니다.
Keith

7
Enum.TryParse는 어떻습니까?
Elaine

15
아주 좋아요 마지막 예제에서 where T : 구조체가 필요합니다.
bbrik

330

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

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

C # 7.0의 매개 변수 유형 인라이닝을 사용 하면 훨씬 간단해질 수 있습니다 .

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

45
대소 문자 구분을 위해 중간 부울 매개 변수를 추가하면 가장 안전하고 가장 우아한 솔루션입니다.
DanM7

18
자, 2008 년부터 선택된 답변을 아래로 스크롤하여 더 나은 (현대적인) 답변으로 구현 한 사람이 몇 명이나 되었습니까?
TEK

@ TEK 실제로 2008 년 답변을 선호합니다.
Zero3

나는 그것을 얻지 못한다. Parse변환 오류 (값이 null비어 있거나 해당 열거 상수가 없음)에 대한 설명 예외가 발생합니다.이 값은 TryParse부울 반환 값 (구체적인 오류를 억제 함) 보다 낫습니다
yair

2
정수 문자열을 구문 분석 할 때 Enum.TryParse <T> (String, T)에 결함이 있습니다. 예를 들어,이 코드는 무의미한 문자열을 무의미한 열거 형으로 성공적으로 구문 분석합니다. var result = Enum.TryParse<System.DayOfWeek>("55", out var parsedEnum);
Mass Dot Net

196

Enum.Parse()리플렉션을 통해 구현되므로 성능 이 끔찍합니다. (의 Enum.ToString반대도 마찬가지입니다 .)

성능에 민감한 코드에서 문자열을 열거 형으로 변환해야하는 경우 가장 좋은 방법은 Dictionary<String,YourEnum>시작시 생성하고 이를 사용하여 변환하는 것입니다.


10
데스크탑 컴퓨터에서 첫 번째 실행에서 문자열을 Enum으로 변환하기 위해 3ms를 측정했습니다. (완전 함의 수준을 설명하기 위해).
Matthieu Charbonnier

12
와우 3ms는 끔찍한 주문입니다
John Stock

1
코드 샘플을 추가해 주시면 교체 및 사용 방법에 대한 아이디어를 얻을 수 있습니다.
Transformer

1 백만 명의 사용자가 앱을 사용하는 경우 => 한 번의 사용으로 최대 50 시간의 인명을 소비합니다. : P
Cătălin Rădoi


31

이제 확장 방법을 사용할 수 있습니다 :

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

그리고 당신은 아래 코드로 그들을 호출 할 수 있습니다 (여기서는 FilterType열거 형입니다) :

FilterType filterType = type.ToEnum<FilterType>();

1
값을 객체로 가져 와서이 메소드 내부의 문자열로 캐스트하도록 이것을 업데이트했습니다. 이 방법으로 문자열 대신 int 값 .ToEnum을 사용할 수 있습니다.
RealSollyM

2
@SollyM 나는 그것이 끔찍한 아이디어의 원인이라고 말하고이 확장 방법은 모든 객체 유형에 적용됩니다 . 하나는 문자열과 int에 대한 두 가지 확장 방법이 더 깨끗하고 안전하다고 생각합니다.
Svish

@Svish, 맞습니다. 내가 한 유일한 이유는 코드가 내부적으로 만 사용되며 2 개의 확장자를 작성하지 않기를 원했기 때문입니다. 그리고 우리가 Enum으로 변환하는 유일한 시간은 string 또는 int 뿐이므로 다른 방법으로는 문제가되지 않았습니다.
RealSollyM

3
@SollyM 내부 여부에 관계없이, 나는 여전히 내 코드를 유지하고 사용하는 사람입니다 : 모든 인텔리 센스 메뉴에서 ToEnum을 깨우면 PI가 짜증날 것입니다. 열거 형으로 변환하는 유일한 시간은 문자열입니다. 또는 int, 당신은 그 두 가지 방법 만 필요할 것이라고 확신 할 수 있습니다. 그리고 두 가지 방법은 그다지 크지 않으며, 특히 작고 유틸리티 유형 인 경우에는 더욱 그렇습니다 .P
Svish

20
object Enum.Parse(System.Type enumType, string value, bool ignoreCase);

따라서 분위기라는 열거 형이 있으면 다음과 같습니다.

   enum Mood
   {
      Angry,
      Happy,
      Sad
   } 

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

18

주의 :

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

Enum.(Try)Parse() 쉼표로 구분 된 여러 인수를 허용하고 이진수 'or'와 결합합니다| . 당신은 이것을 비활성화 할 수 없으며 내 의견으로는 거의 그것을 원하지 않습니다.

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

Three정의되지 않은 경우에도 x여전히 int value를 얻습니다 3. 더 나쁘다 : Enum.Parse ()는 열거 형에 대해 정의되지 않은 값을 줄 수 있습니다!

기꺼이 또는 의지하지 않고 사용자의 결과를 경험하고 싶지 않아이 행동을 유발하고 싶습니다.

또한 다른 사람들이 언급했듯이 성능은 큰 열거 형에 이상적입니다. 즉 가능한 값 수에 선형 적입니다.

나는 다음을 제안한다.

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

4
사실 이것은 이것을 아는 것이 매우 유용합니다 Enum.(Try)Parse accepts multiple, comma-separated arguments, and combines them with binary 'or'. 열거 형 값을 2의 거듭 제곱으로 설정할 수 있으며 여러 부울 플래그를 구문 분석하는 매우 쉬운 방법이 있습니다. "UseSSL, NoRetries, Sync". 실제로 그것은 아마도 그것이 설계된 것입니다.
pcdev

16

Enum.Parse 는 당신의 친구입니다 :

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

13

예외를 피하기 위해 허용 된 답변을 기본값으로 확장 할 수 있습니다.

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

그런 다음 다음과 같이 호출합니다.

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

기본값이 열거 형이 아닌 경우 Enum.TryParse가 실패하고 포착 된 예외가 발생합니다.

수년간 코드에서이 함수를 여러 곳에서 사용한 후에는이 작업에 성능이 필요한 정보를 추가하는 것이 좋습니다!


나는 기본값을 좋아하지 않는다. 예기치 않은 결과가 발생할 수 있습니다.
Daniël Tulp

5
언제 예외가 발생합니까?
andleer

열거 형 값이 기본값과 동일한 열거 형에 맞지 않으면 @andleer
Nelly

@Nelly 이전 코드는 여기에 있지만 defaultValue메소드 반환 유형은 모두 유형 T입니다. 유형이 다른 경우 컴파일 시간 오류가 나타납니다. " 'ConsoleApp1.Size'에서 'ConsoleApp1.Color'로 변환 할 수 없습니다."
andleer

@andleer, 마지막 답변이 잘못되었습니다. 누군가 enum 유형이 아닌 기본값으로이 함수를 호출하는 경우이 메소드는 Syste.ArgumentException을 발생시킬 수 있습니다. c # 7.0에서는 T : Enum의 where 절을 만들 수 없습니다. 그래서 나는 시도 캐치 로이 가능성을 잡았습니다.
Nelly

8

우리는 완벽하게 유효한 입력을 가정 할 수 없었으며 @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;
}

7
// str.ToEnum<EnumType>()
T static ToEnum<T>(this string str) 
{ 
    return (T) Enum.Parse(typeof(T), str);
}

5

try / catch 및 .NET 4.5의 TryParse () 메서드없이 문자열을 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;
}

1
코드에 이미 설명이 포함 된 경우 설명을 작성해야하는지 여부 좋아, 나는 이것을했다 :)
jite.gs

3

TryParse를 사용하는 매우 간단한 코드 :

var value = "Active";

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

2

나는 확장 방법 솔루션을 좋아한다 ..

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

다음은 테스트로 구현 한 것입니다.

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
        }

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

==================== 완전한 프로그램 ====================

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

클래스 (구문 분석 및 성능 향상과 함께 강력한 형식의 Enum 버전)를 사용했습니다. GitHub에서 찾았으며 .NET 3.5에서도 작동합니다. 사전을 버퍼링하기 때문에 약간의 메모리 오버 헤드가 있습니다.

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

블로그 게시물은 Enums – NET 3.5의 향상된 구문, 향상된 성능 및 TryParse입니다 .

그리고 코드 : https://github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/System/EnumT.cs


1

성능 향상을 위해 다음이 도움이 될 수 있습니다.

    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

EnumMember 값을 가진 enum 값이있는 경우는 고려되지 않았습니다. 그래서 우리는 간다 :

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

그리고 그 열거의 예 :

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

1

Enum에서 개체 값을 가져 오려면 Enum.Parse를 사용해야합니다. 그런 다음 개체 값을 특정 열거 형 값으로 변경해야합니다. 열거 형 값으로 캐스트는 Convert.ChangeType을 사용하여 수행 할 수 있습니다. 다음 코드 스 니펫을 살펴보십시오.

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

1

이 샘플을 사용해보십시오 :

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

이 샘플에서는 모든 문자열을 보내고을 설정할 수 있습니다 Enum. 귀하의 경우 Enum한 데이터는 당신이 원하는 것을, 당신으로 그 반환 Enum유형입니다.


1
newModel각 줄을 덮어 쓰므 로 대시가 포함되어 있으면 바뀌지 않습니다. 또한 문자열에 아무것도 포함되어 있는지 확인할 필요가 없습니다 Replace. 어쨌든 전화 할 수 있습니다 .var newModel = model.Replace("-", "").Replace(" ", "");
Lars Kristensen

@LarsKristensen 그래, 영숫자가 아닌 문자를 제거하는 메소드를 만들 수있다.
AmirReza-Farahlagha

1

이것이 언제 추가되었는지 확실하지 않지만 Enum 클래스에는 이제

Parse<TEnum>(stringValue)

문제의 예와 같이 사용됩니다.

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

또는 다음을 통해 케이스를 무시합니다.

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

이것이 사용하는 디 컴파일 된 메소드는 다음과 같습니다.

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

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

0
public TEnum ToEnum<TEnum>(this string value, TEnum defaultValue){
if (string.IsNullOrEmpty(value))
    return defaultValue;

return Enum.Parse(typeof(TEnum), value, true);}

0

속성 이름이 호출하려는 것과 다른 경우 (예 : 언어 차이) 다음과 같이 할 수 있습니다.

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
    }     
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.