Enum 값을 TryParse하는 방법은 무엇입니까?


94

가능한 값에 대해 주어진 값 (문자열로 전달됨)의 유효성을 검사 할 수있는 함수를 작성하고 싶습니다 enum. 일치하는 경우 열거 형 인스턴스를 반환해야합니다. 그렇지 않으면 기본값을 반환해야합니다.

함수는 내부적으로 try/를 사용하지 않을 수 있습니다 . 이는 유효하지 않은 인수가 주어지면 예외를 발생시키는 catchusing을 제외합니다 Enum.Parse.

이것을 TryParse구현하기 위해 함수 라인을 따라 무언가를 사용하고 싶습니다 .

public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
   object enumValue;
   if (!TryParse (typeof (TEnum), strEnumValue, out enumValue))
   {
       return defaultValue;
   }
   return (TEnum) enumValue;
}

8
나는이 질문을 이해하지 못한다. "나는이 문제를 해결하고 싶지만 나에게 해결책을 줄 수있는 방법을 사용하고 싶지 않습니다."라고 말하는 것입니다. 점은 무엇인가?
Domenic

1
시도 / 잡기 솔루션에 대한 혐오감은 무엇입니까? '비용이 많이 들기 때문에 예외'를 피하려는 경우 휴식을 취하십시오. 99 %의 경우 비용 예외 발생 / 캐치 비용은 기본 코드에 비해 무시할 수 있습니다.
SolutionYogi

1
예외 처리 비용은 그리 나쁘지 않습니다. 지옥,이 모든 열거 변환의 내부 구현은 예외 처리로 가득 차 있습니다. 나는 정상적인 응용 프로그램 논리 중에 예외가 발생하고 포착되는 것을 정말 싫어합니다. 던져진 모든 예외를 중단하는 것이 때때로 유용 할 수 있습니다 (잡힌 경우에도). 사방에 예외를 던지면 사용하기가 훨씬 더 성
가실

3
@Domenic : 이미 알고있는 것보다 더 나은 솔루션을 찾고 있습니다. 이미 알고있는 노선이나 기차를 요청하기 위해 철도 문의에 가보시겠습니까 :).
Manish Basantani

2
@Amby, 단순히 try / catch 블록을 입력하는 비용은 무시할 수 있습니다. 예외를 던지는 비용은 아니지만 예외적이어야합니다. 또한 "we never know"라고 말하지 마십시오 ... 코드를 프로파일 링하고 알아 내십시오. 뭔가 느린 지 궁금해서 시간을 낭비하지 말고 FIND OUT!
akmad

답변:


31

다른 사람들이 말했듯이 자신 만의 TryParse. Simon Mourier는 모든 것을 처리하는 완전한 구현을 제공합니다.

비트 필드 열거 형 (즉 플래그)을 사용하는 경우 "MyEnum.Val1|MyEnum.Val2"두 열거 형 값의 조합 인 문자열도 처리해야 합니다. Enum.IsDefined이 문자열로 호출 Enum.Parse하면 올바르게 처리 하더라도 false를 반환 합니다.

최신 정보

주석에서 Lisa와 Christian이 언급했듯이 Enum.TryParse이제 .NET4 이상에서 C #을 사용할 수 있습니다.

MSDN 문서


아마도 가장 섹시하지는 않지만 코드가 .NET 4로 마이그레이션 될 때까지 이것이 확실히 최고라는 데 동의합니다.
Lisa

1
아래에 언급했지만 실제로는 보이지 않습니다. .Net 4부터 Enum.TryParse를 사용할 수 있으며 추가 코딩없이 작동합니다. 자세한 내용은 MSDN에서 확인할 수 있습니다. msdn.microsoft.com/library/vstudio/dd991317%28v=vs.100%29.aspx
Christian

106

Enum.IsDefined는 작업을 완료합니다. TryParse만큼 효율적이지 않을 수 있지만 예외 처리없이 작동합니다.

public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
    if (!Enum.IsDefined(typeof(TEnum), strEnumValue))
        return defaultValue;

    return (TEnum)Enum.Parse(typeof(TEnum), strEnumValue);
}

주목할만한 점 TryParse은 .NET 4.0에 메서드가 추가되었습니다.


1
내가 지금까지 본 적이 우수 답변 ... 아니 시도 / 캐치, 아니 GetNames :
토마스 레베


6
또한 더 다음 IsDefined에 경우가 무시되지 않습니다
안토니 존스턴에게

2
@Anthony : 케이스 무감각을 지원하려면 GetNames. 내부적으로 이러한 모든 메서드 (포함 Parse) GetHashEntry는 실제 반사를 한 번 수행하는를 사용합니다. 밝은면에서 .NET 4.0에는 TryParse가 있으며 일반도 마찬가지입니다. :)
Thorarin

+1 그것은 내 하루를 구했습니다! 나는 .NET 4에서 .NET 3.5로 많은 코드를 백
포팅

20

다음은 EnumTryParse. 다른 일반적인 구현과 달리 Flags속성으로 표시된 열거 형도 지원 합니다.

    /// <summary>
    /// Converts the string representation of an enum to its Enum equivalent value. A return value indicates whether the operation succeeded.
    /// This method does not rely on Enum.Parse and therefore will never raise any first or second chance exception.
    /// </summary>
    /// <param name="type">The enum target type. May not be null.</param>
    /// <param name="input">The input text. May be null.</param>
    /// <param name="value">When this method returns, contains Enum equivalent value to the enum contained in input, if the conversion succeeded.</param>
    /// <returns>
    /// true if s was converted successfully; otherwise, false.
    /// </returns>
    public static bool EnumTryParse(Type type, string input, out object value)
    {
        if (type == null)
            throw new ArgumentNullException("type");

        if (!type.IsEnum)
            throw new ArgumentException(null, "type");

        if (input == null)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        input = input.Trim();
        if (input.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        string[] names = Enum.GetNames(type);
        if (names.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        Type underlyingType = Enum.GetUnderlyingType(type);
        Array values = Enum.GetValues(type);
        // some enums like System.CodeDom.MemberAttributes *are* flags but are not declared with Flags...
        if ((!type.IsDefined(typeof(FlagsAttribute), true)) && (input.IndexOfAny(_enumSeperators) < 0))
            return EnumToObject(type, underlyingType, names, values, input, out value);

        // multi value enum
        string[] tokens = input.Split(_enumSeperators, StringSplitOptions.RemoveEmptyEntries);
        if (tokens.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        ulong ul = 0;
        foreach (string tok in tokens)
        {
            string token = tok.Trim(); // NOTE: we don't consider empty tokens as errors
            if (token.Length == 0)
                continue;

            object tokenValue;
            if (!EnumToObject(type, underlyingType, names, values, token, out tokenValue))
            {
                value = Activator.CreateInstance(type);
                return false;
            }

            ulong tokenUl;
            switch (Convert.GetTypeCode(tokenValue))
            {
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.SByte:
                    tokenUl = (ulong)Convert.ToInt64(tokenValue, CultureInfo.InvariantCulture);
                    break;

                //case TypeCode.Byte:
                //case TypeCode.UInt16:
                //case TypeCode.UInt32:
                //case TypeCode.UInt64:
                default:
                    tokenUl = Convert.ToUInt64(tokenValue, CultureInfo.InvariantCulture);
                    break;
            }

            ul |= tokenUl;
        }
        value = Enum.ToObject(type, ul);
        return true;
    }

    private static char[] _enumSeperators = new char[] { ',', ';', '+', '|', ' ' };

    private static object EnumToObject(Type underlyingType, string input)
    {
        if (underlyingType == typeof(int))
        {
            int s;
            if (int.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(uint))
        {
            uint s;
            if (uint.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(ulong))
        {
            ulong s;
            if (ulong.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(long))
        {
            long s;
            if (long.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(short))
        {
            short s;
            if (short.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(ushort))
        {
            ushort s;
            if (ushort.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(byte))
        {
            byte s;
            if (byte.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(sbyte))
        {
            sbyte s;
            if (sbyte.TryParse(input, out s))
                return s;
        }

        return null;
    }

    private static bool EnumToObject(Type type, Type underlyingType, string[] names, Array values, string input, out object value)
    {
        for (int i = 0; i < names.Length; i++)
        {
            if (string.Compare(names[i], input, StringComparison.OrdinalIgnoreCase) == 0)
            {
                value = values.GetValue(i);
                return true;
            }
        }

        if ((char.IsDigit(input[0]) || (input[0] == '-')) || (input[0] == '+'))
        {
            object obj = EnumToObject(underlyingType, input);
            if (obj == null)
            {
                value = Activator.CreateInstance(type);
                return false;
            }
            value = obj;
            return true;
        }

        value = Activator.CreateInstance(type);
        return false;
    }

1
귀하는 최상의 구현을 제공했으며 내 목적으로 사용했습니다. 그러나 왜 Activator.CreateInstance(type)기본 열거 형 값을 만드는 데 사용하는지 궁금합니다 Enum.ToObject(type, 0). 맛의 문제?
Pierre Arnaud 2011 년

1
@Pierre-흠 ... 아니, 그 당시에는 더 자연스러워 보였습니다. :-) 내부적으로 InternalBoxEnum 호출을 사용하기 때문에 Enum.ToObject가 더 빠를까요? 그 ... 확인하지
사이먼 Mourier

2
아래에 언급했지만 실제로는 보이지 않습니다. .Net 4부터 Enum.TryParse를 사용할 수 있으며 추가 코딩없이 작동합니다. 자세한 내용은 MSDN에서 확인할 수 있습니다. msdn.microsoft.com/library/vstudio/dd991317%28v=vs.100%29.aspx
Christian

16

결국 이것을 구현해야합니다 Enum.GetNames.

public bool TryParseEnum<T>(string str, bool caseSensitive, out T value) where T : struct {
    // Can't make this a type constraint...
    if (!typeof(T).IsEnum) {
        throw new ArgumentException("Type parameter must be an enum");
    }
    var names = Enum.GetNames(typeof(T));
    value = (Enum.GetValues(typeof(T)) as T[])[0];  // For want of a better default
    foreach (var name in names) {
        if (String.Equals(name, str, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)) {
            value = (T)Enum.Parse(typeof(T), name);
            return true;
        }
    }
    return false;
}

추가 참고 사항 :

  • Enum.TryParse.NET 4에 포함되어 있습니다 . 여기를 참조하십시오. http://msdn.microsoft.com/library/dd991876(VS.100).aspx
  • 또 다른 접근 방식은 Enum.Parse실패 할 때 throw되는 예외를 포착하여 직접 래핑하는 것 입니다. 일치하는 항목이 발견되면 더 빠를 수 있지만 그렇지 않은 경우 속도가 느려질 수 있습니다. 처리중인 데이터에 따라 이것은 순 개선 일 수도 있고 아닐 수도 있습니다.

편집 : 필요한 정보를 캐시하는 더 나은 구현을 보았습니다. http://damieng.com/blog/2010/10/17/enums-better-syntax-improved-performance-and-tryparse-in-net- 3-5


default (T)를 사용하여 기본값을 설정하는 것이 좋습니다. 이것은 모든 열거 형에 대해 작동하지 않는 것으로 나타났습니다. 예를 들어 enum의 기본 유형이 int이면 default (T)는 항상 0을 반환하며 이는 열거 형에 대해 유효하거나 유효하지 않을 수 있습니다.
Daniel Ballinger

Damieng 블로그의 구현 은 속성 이있는 열거 형을 지원 하지 않습니다Flags .
Uwe Keim 2013

9

.NET 4.5 기반

아래 샘플 코드

using System;

enum Importance
{
    None,
    Low,
    Medium,
    Critical
}

class Program
{
    static void Main()
    {
    // The input value.
    string value = "Medium";

    // An unitialized variable.
    Importance importance;

    // Call Enum.TryParse method.
    if (Enum.TryParse(value, out importance))
    {
        // We now have an enum type.
        Console.WriteLine(importance == Importance.Medium);
    }
    }
}

참조 : http://www.dotnetperls.com/enum-parse


4

UnconstrainedMelody 에서 사용할 수있는 최적화 된 구현이 있습니다 . 실제로는 이름 목록을 캐싱하는 것이지만 멋지고 강력하게 형식화되고 일반적으로 제한되는 방식으로 수행됩니다. :)


4
enum EnumStatus
{
    NAO_INFORMADO = 0,
    ENCONTRADO = 1,
    BLOQUEADA_PELO_ENTREGADOR = 2,
    DISPOSITIVO_DESABILITADO = 3,
    ERRO_INTERNO = 4,
    AGARDANDO = 5
}

...

if (Enum.TryParse<EnumStatus>(item.status, out status)) {

}

2

현재 즉시 사용 가능한 Enum.TryParse는 없습니다. 이것은 Connect ( Still no Enum.TryParse ) 에서 요청되었으며 .NET 3.5 이후 다음 프레임 워크에 포함될 수 있음을 나타내는 응답을 받았습니다. 지금은 제안 된 해결 방법을 구현해야합니다.


1

예외 처리를 피하는 유일한 방법은 GetNames () 메서드를 사용하는 것입니다. 우리 모두는 일반적인 응용 프로그램 논리에 대해 예외를 남용해서는 안된다는 것을 알고 있습니다.


1
유일한 방법 은 아닙니다 . Enum.IsDefined (..)는 사용자 코드에서 예외가 throw되는 것을 방지합니다.
Thorarin

1

동적으로 생성 된 함수 / 사전을 캐싱 할 수 있습니까?

열거 형의 유형을 미리 알지 못하기 때문에 (보여지는 것처럼) 첫 번째 실행은 후속 실행에서 활용할 수있는 무언가를 생성 할 수 있습니다.

Enum.GetNames ()의 결과를 캐시 할 수도 있습니다.

CPU 또는 메모리 최적화를 시도하고 있습니까? 당신이 할 정말 필요?


아이디어는 CPU를 최적화하는 것입니다. 비용 메모리로 할 수 있다는 데 동의합니다. 그러나 내가 찾고있는 해결책이 아닙니다. 감사.
Manish Basantani

0

다른 사람들이 이미 말했듯이 Try & Catch를 사용하지 않으면 IsDefined 또는 GetNames를 사용해야합니다. 여기 몇 가지 샘플이 있습니다. 기본적으로 모두 동일하며 첫 번째는 nullable 열거 형을 처리합니다. 열거 형이 아닌 문자열의 확장이므로 두 번째를 선호하지만 원하는대로 혼합 할 수 있습니다!

  • www.objectreference.net/post/Enum-TryParse-Extension-Method.aspx
  • flatlinerdoa.spaces.live.com/blog/cns!17124D03A9A052B0!605.entry
  • mironabramson.com/blog/post/2008/03/Another-version-for-the-missing-method-EnumTryParse.aspx
  • lazyloading.blogspot.com/2008/04/enumtryparse-with-net-35-extension.html

0

Enum의 유형은 런타임까지 알 수 없기 때문에 TryParse가 없습니다. Date.TryParse 메서드와 동일한 방법론을 따르는 TryParse는 ByRef 매개 변수에 암시 적 변환 오류를 발생시킵니다.

다음과 같이하는 것이 좋습니다.

//1 line call to get value
MyEnums enumValue = (Sections)EnumValue(typeof(Sections), myEnumTextValue, MyEnums.SomeEnumDefault);

//Put this somewhere where you can reuse
public static object EnumValue(System.Type enumType, string value, object NotDefinedReplacement)
{
    if (Enum.IsDefined(enumType, value)) {
        return Enum.Parse(enumType, value);
    } else {
        return Enum.Parse(enumType, NotDefinedReplacement);
    }
}

들면 Try그 결과 방법 value 타입 일 수도 있고, 여기서 null정당한 결과 일 수있다 (예 Dictionary.TryGetValue, which has both such traits), the normal pattern is for a Try` 방법 돌아 bool와, 같은 결과를 전달 out파라미터. 것들 답례 클래스 유형 곳이 null유효한 결과 아니다하는 사용하는데 어려움이 없다 null반환 실패를 나타냅니다.
supercat dec

-1

Enum 클래스 (struct?) 자체를 살펴보십시오. 그것에 대한 Parse 메서드가 있지만 tryparse에 대해 잘 모르겠습니다.


Enum.Parse (typeof (TEnum), strEnumValue) 메서드에 대해 알고 있습니다. strEnumValue가 유효하지 않으면 ArgumentException이 발생합니다. ........ TryParse를 찾고
마니 Basantani

-2

이 메서드는 열거 형 유형을 변환합니다.

  public static TEnum ToEnum<TEnum>(object EnumValue, TEnum defaultValue)
    {
        if (!Enum.IsDefined(typeof(TEnum), EnumValue))
        {
            Type enumType = Enum.GetUnderlyingType(typeof(TEnum));
            if ( EnumValue.GetType() == enumType )
            {
                string name = Enum.GetName(typeof(HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue);
                if( name != null)
                    return (TEnum)Enum.Parse(typeof(TEnum), name);
                return defaultValue;
            }
        }
        return (TEnum)Enum.Parse(typeof(TEnum), EnumValue.ToString());
    } 

기본 유형을 확인하고 구문 분석 할 이름을 가져옵니다. 모든 것이 실패하면 기본값을 반환합니다.


3
이 작업은 "Enum.GetName (typeof (HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue)"아마도 로컬 코드에 대한 약간의 종속성입니다.
Manish Basantani 2010 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.