995

확장 함수를 작성 중입니다.Enum.Parse그 개념

  • Enum 값을 찾을 수없는 경우 기본값을 파싱 할 수 있습니다.
  • 대소 문자를 구분하지 않습니다.

그래서 나는 다음과 같이 썼다.

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

오류 제한이 특별 수업이 될 수 없습니다.System.Enum.

공평하지만, 일반 열거 형을 허용하는 해결 방법이 있습니까, 아니면 일반 열거 형을 모방해야합니다.Parse함수를 사용하고 코드로 추한 복싱 요구 사항을 강제로 특성으로 유형을 전달합니다.

편집하다아래의 모든 제안을 크게 감사드립니다.

정착했다 (나는 대소 문자를 구분하지 않기 위해 루프를 떠났다. 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;
    }
}

편집하다:(2015 년 2 월 16 일) Julien Lebosquain이 최근에 게시했습니다.컴파일러가 MSIL 또는 F #아래는 잘 볼만한 가치가 있으며 upvote입니다. 솔루션이 페이지 위로 올라가는 경우이 편집을 제거합니다.


  • 아마도 너사용해야한다ToLower () 대신 ToUpperInvariant () ... - Max Galkin
  • @ Shimmy : 확장 메소드에 값 유형을 전달하자마자 사본을 작업 중이므로 상태를 변경할 수 없습니다. - Garo Yeriazarian
  • 오래된 스레드인지, 변경되었는지는 알 수 없지만 확장 메서드는 값 유형에 적합합니다. 항상 의미가있는 것은 아니지만 반드시 " public static TimeSpan Seconds (this int x) {return TimeSpan.FromSeconds (x); } " " Wait.For (5.Seconds ()) ... "의 구문을 사용하도록 설정합니다. - Jens
  • 이것이 문제는 아니지만 StringComparison.InvariantCultureIgnoreCase와 함께 String.Equals를 사용하여 foreach 루프 논리를 향상시킬 수 있습니다. - Firestrand
  • 가능한 중복누구나 열거 형 제네릭 제약의 부족에 대한 좋은 해결 방법을 알고 있습니까? - nawfal

20 답변


901

이후Enum유형 구현IConvertible인터페이스를 구현하려면 다음과 같이 구현해야합니다.

public T GetEnumFromString<T>(string value) where T : struct, IConvertible
{
   if (!typeof(T).IsEnum) 
   {
      throw new ArgumentException("T must be an enumerated type");
   }

   //...
}

이것은 여전히 구현하는 값 유형의 전달을 허용합니다.IConvertible. 기회는 드뭅니다.


  • Generics는 .NET 2.0부터 사용할 수 있습니다. 따라서 그들은 2005 년에도 사용할 수 있습니다. - Vivek
  • 글쎄, 당신이이 길을 가기로 결심했다면, 더 제약적으로 만드십시오 ... "class TestClass & lt; T & gt;를 사용하십시오. 여기서 T : struct, IComparable, IFormattable, IConvertible " - Ricardo Nolde
  • 또 다른 제안은 TEnum 식별자로 제네릭 형식을 정의하는 것입니다. 따라서 : TEnum : struct, IConvertible, IComparible, IFormattable {} 여기서 TEnum GetEnumFromString & lt; TEnum & gt; (문자열 값) - Lisa
  • 거의 모든 기본 제공 값 유형이 모든 인터페이스를 구현하므로 다른 인터페이스를 포함하면 많은 이점을 얻을 수 없습니다. 이는 확장 메서드가 모든 개체를 감염시키는 바이러스와 같은 사실을 제외하면 열거 형에서 작동하는 데 매우 유용한 일반적인 확장 메서드의 제약 조건에 특히 해당됩니다. IConvertable은 적어도 그것을 상당히 좁 힙니다. - russbishop
  • @SamIam : 게시했을 때,이 스레드는 6 살반이었고, 당신이 옳았습니다. 어떤 컴파일 타임에서도 검사하지 않았습니다. 6 년 후에 3 일 후에 Julien Lebosquain의 게시 방법을 아래에서 확인하십시오. - David I. McIntosh

472

이 기능은 마침내 C # 7.3에서 지원됩니다!

다음 스 니펫 (에서닷넷 샘플)는 다음을 사용함을 보여줍니다.

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

C # 프로젝트의 언어 버전을 버전 7.3으로 설정하십시오.


원문 아래의 답변 :

나는 게임에 늦었지만 어떻게 할 수 있는지보기 위해 도전으로 생각했다. C # (또는 VB.NET에서는 가능하지 않지만 F #에서는 아래로 스크롤)가능하다MSIL에서. 나는이 작은 .... 것을 썼다.

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

어떤 함수를 생성할 것이다그것이 C #이라면 이렇게 보일 것입니다.

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

다음 C # 코드를 사용합니다.

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
}

불행히도 이것은 C # 대신 MSIL로 작성된 코드의 일부분을 갖는 것입니다. 단, 추가 된 이점은이 메소드를 다음과 같이 제한 할 수 있다는 것입니다.System.Enum. 별도의 어셈블리로 컴파일되기 때문에 일종의 허풍입니다. 그러나 그런 식으로 배포해야한다는 의미는 아닙니다.

라인을 제거함으로써.assembly MyThing{}다음과 같이 ilasm을 호출합니다.

ilasm.exe /DLL /OUTPUT=MyThing.netmodule

어셈블리 대신 netmodule을 얻습니다.

불행하게도, VS2010 (그리고 이전 버전에서는 분명히)은 netmodule 참조를 추가 할 수 없기 때문에 디버깅 할 때 두 개의 별도 어셈블리에 두어야합니다. 어셈블리의 일부로 추가 할 수있는 유일한 방법은 csc.exe를 직접 실행하는 것입니다./addmodule:{files}명령 행 인수. 그것은 없을거야.너무고통스러운 MSBuild 스크립트. 물론, 용감하거나 바보 같은 사람이라면 매번 수동으로 csc를 실행할 수 있습니다. 또한 여러 어셈블리가 액세스해야하기 때문에 더욱 복잡해집니다.

그래서 .Net에서 할 수 있습니다. 그것은 여분의 노력의 가치가 있니? 음, 그걸 결정하게 내버려 두 겠네.


대안으로 F # 솔루션

추가 크레딧 :enumMSIL : F # 외에 적어도 하나 이상의 다른 .NET 언어에서도 가능합니다.

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

이 도구는 Visual Studio IDE를 완벽하게 지원하는 잘 알려진 언어이므로 유지 관리가 더 쉽지만 솔루션에 별도의 프로젝트가 필요합니다. 그러나 자연스럽게 다른 IL을 생성합니다 (코드~이다.매우 다른).FSharp.Core라이브러리는 다른 외부 라이브러리와 마찬가지로 배포본의 일부가되어야합니다.

여기서는 (MSIL 솔루션과 기본적으로) 동일한 방법으로 사용할 수 있으며, 동의어가있는 구조체에서 올바르게 실패했음을 보여줍니다.

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


  • 그래, 아주 열심히. 나는 일리노이 주에서 코드 작성을 할 수있는 사람을 존중하며,기능이 더 높은 언어 수준에서 지원되는 방법을 알아야합니다. 많은 사람들이 응용 프로그램, 비즈니스 규칙, UI 및 구성 요소 라이브러리 등에서 여전히 낮은 수준으로보고있는 수준입니다. - TonyG
  • 내가 정말로 알고 싶은 것은 C # 팀이 MSIL에서 이미 지원 되었기 때문에 C # 팀이 아직 이것을 허용하지 않았기 때문입니다. - MgSam
  • @MgSam - 올린 사람에릭 리 퍼트:There's no particularly unusual reason why not; we have lots of other things to do, limited budgets, and this one has never made it past the "wouldn't this be nice?" discussion in the language design team. - Christopher Currens
  • @LordofScripts : 이유는 그 이유는 클래스를TSystem.Enum모든 일을 할 수 없다면T사람들이 기대할지도 모르는 C #의 저자들은 모두 그것을 금지 할 수 있다고 생각했습니다. 나는 그 결정이 불행하다고 생각한다. 왜냐하면 C #은 단순히 특별한 처리를 무시했다.System.Enum제약 조건을 사용하면HasAnyFlags<T>(this T it, T other)확장 방법은Enum.HasFlag(Enum)인수를 유형 검사했다. - supercat
  • 여기서 끝나지 않은 프로젝트가 있다고 생각하지 않습니다. C # 6은 110 %의 통사 성 설탕이고 이것은 들어 가지 못했습니까? 쓰레기 줄이자. - Michael Blackburn

163

C # ≥ 7.3

C # 7.3 (Visual Studio 2017 ≥ v15.7에서 사용 가능)부터이 코드는 이제 완전히 유효합니다.

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

C # ≤ 7.2

제약 상속을 남용하여 실제 컴파일러가 enum 제약을 적용 할 수 있습니다. 다음 코드는classstruct동시에 제약 조건 :

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

용법:

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

참고 :이 내용은 C # 5.0 언어 사양에 명시되어 있습니다.

타입 파라미터 S가 타입 파라미터 T에 의존하는 경우,   [...]   S는 값 타입 제약을 가지며 T는 참조 타입을 갖는다.   강제. 효과적으로 이것은 T를 System.Object 유형으로 제한합니다.   System.ValueType, System.Enum 및 모든 인터페이스 유형


  • @ DavidI.McIntoshEnumClassUtils<System.Enum>T를 임의의 것으로 제한하기에 충분하다.System.Enum및 파생 된 유형.struct...에Parse그런 다음 그것을 실제 enum 유형으로 더 제한합니다. 제한해야합니다.Enum어떤 시점에서. 이렇게하려면 클래스를 중첩해야합니다. 만나다gist.github.com/MrJul/7da12f5f2d6c69f03d79 - Julien Lebosquain
  • 그냥 분명히하기 위해, 내 코멘트 "쾌활하지 않다" 귀하의 솔루션에 대한 코멘트가 아니 었습니다. 정말 아름다운 해킹입니다. "쾌적하지 않다"는 것만으로 MS는 그러한 복잡한 해킹을 사용하도록 강요합니다. - David I. McIntosh
  • 확장 메서드에도 사용할 수 있도록이 방법을 사용할 수 있습니까? - Mord Zuber
  • 이 클래스가 이상한 방식으로 상속되지 않도록 내부 생성자를 추가합니다. - John Gietzen
  • 무엇이where TClass : class제약 이득은 여기? - tsemer

29

편집하다

이제 질문은 다음과 같이 훌륭하게 답변되었습니다.줄리앙 레 보스 퀘인. 나는 또한 그의 대답을 연장하고 싶다.ignoreCase,defaultValue선택적 인수를 추가하는 동안TryParseParseOrDefault.

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
}

사용 예 :

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

늙은

내 이전 개선 사항비벡의 대답의견 및 '새로운'개발을 사용하여 :

  • 용도TEnum사용자의 편의를 위해
  • 추가 제약 조건 검사를위한 인터페이스 제약 조건 추가
  • 방해TryParse핸들ignoreCase기존 매개 변수로 (VS2010 / NET 4에서 소개)
  • 선택적으로 제네릭을 사용합니다.default(VS2005 / .Net 2에서 소개)
  • 용도선택적 인수(VS2010 / .Net 4에서 소개 된) 디폴트 값, fordefaultValueignoreCase

를 야기하는:

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


18

T 유형이 열거 형인지 점검하고 그렇지 않은 경우 예외를 throw하는 클래스에 정적 생성자를 정의 할 수 있습니다. Jeffery Richter가 C #을 통해 저서 인 CLR에서 언급 한 방법입니다.

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

그런 다음 구문 분석 메서드에서 Enum.Parse (typeof (T), input, true)를 사용하여 문자열에서 열거 형으로 변환 할 수 있습니다. 마지막 true 매개 변수는 입력의 대소 문자를 무시하기위한 것입니다.


  • 이는 일반적인 클래스에 대한 좋은 옵션입니다. 물론 일반 메소드에 대해서는 도움이되지 않습니다. - McGarnagle
  • 또한 이것은 컴파일 타임에 적용되지 않으며, 당신은 오직 당신이Enum T생성자가 실행될 때. 이것은 인스턴스 생성자를 기다리는 것보다 훨씬 좋지만. - jrh

11

나는 샘플을 dimarzionist로 수정했다. 이 버전은 Enums에서만 작동하며 구조체를 통과시키지 않습니다.

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


  • 실패하면 기본값을 반환하지 않습니다. 예외를 전파하도록합니다 (Enum.Parse와 마찬가지로). 대신 TryParse를 사용하여 bool을 반환하고 out 매개 변수를 사용하여 결과를 반환합니다. - Mark Simpson
  • OP는 대소 문자를 구분하지 않으려하지만, 그렇지 않습니다. - Konrad Morawski
  • 대소 문자를 구별하지 않으므로 마지막 & # 39; 실제 & # 39; ~을위한 ... - increddibelly

9

코드를 약간 개선하려고했습니다.

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


  • 이것은 전화를 걸 수 있기 때문에 받아 들인 대답보다 낫습니다.defaultValue.ToString("D", System.Globalization.NumberFormatInfo.CurrentInfo)어떤 종류의 열거 형인지 알지 못하더라도 그 열거 형 만이 열거 형입니다. - styfle
  • 사전 진단은IsDefined그래도 케이스의 무감각 함을 망칠 것입니다. 같지 않은Parse,IsDefined없다ignoreCase논의,MSDN은 정확한 경우에만 일치한다고 말합니다.. - Nyerguds

9

또한 Enum 제약 조건을 사용하는 C # 7.3의 릴리스는 추가 검사 및 작업을 수행하지 않고도 즉시 사용할 수 있도록 지원됩니다.

앞으로도 C # 7.3으로 프로젝트의 언어 버전을 변경했다면 다음 코드가 완벽하게 작동합니다.

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

언어 버전을 C # 7.3으로 변경하는 방법을 모르는 경우 다음 스크린 샷을 참조하십시오.enter image description here

편집 1 - 필요한 Visual Studio 버전 및 ReSharper 고려

Visual Studio에서 새 구문을 인식하려면 최소한 버전 15.7이 필요합니다. Microsoft의 릴리스 정보에서도 언급 된 것을 볼 수 있습니다.Visual Studio 2017 15.7 릴리스 정보. 이 유효한 질문을 지적 해 주신 @MohamedElshawaf에게 감사드립니다.

Pls는 또한 필자의 경우 ReSharper 2018.1을 쓰는 시점에서이 EDIT가 아직 C # 7.3을 지원하지 않는다고 언급합니다. ReSharper를 활성화하면 Enum 제약 조건이 나에게 오류로 강조 표시됩니다.형식 매개 변수 제약 조건으로 'System.Array', 'System.Delegate', 'System.Enum', 'System.ValueType', 'object'를 사용할 수 없습니다.. ReSharper는 빠른 수정으로 제안합니다.메서드의 매개 변수 T의 'Enum'제약 조건 제거

그러나 ReSharper를 일시적으로 사용 중지 한 경우도구 -> 옵션 -> ReSharper Ultimate -> 일반VS 15.7 이상 및 C # 7.3 이상을 사용할 경우 문법이 완벽하게 괜찮음을 알 수 있습니다.


  • 어떤 VS 버전을 사용하고 있습니까? - mshwf
  • 이것은 이제 허용 된 대답이어야합니다. - Patrick Roberts
  • @MohamedElshawaf 나는 C # 7.3에 대한 지원을 포함하고있는 버전 15.7을 믿고있다. - Patrick Roberts
  • 나는 글을 쓰는 것이 낫다고 생각한다.where T : struct, Enum, 지나가는 것을 피하기 위해System.Enum그 자체가 타입 파라미터로서. - Mariusz Pawelski
  • @MariuszPawelski처럼 내가 쓴다.struct, Enum. 내 근거는 해답과 설명에 설명되어 있습니다.이리. - Stephen Kennedy

5

enum 값과 관련된 텍스트와 함께 enum을 사용해야하는 특정 요구 사항이 있습니다. 예를 들어 enum을 사용하여 오류 유형을 지정하면 오류 세부 정보를 설명해야합니다.

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


4

희망이 도움이 :

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


  • 대소 문자를 구분할 필요가 없다면return (TValue)Enum.Parse(typeof (TValue), value);으로return (TValue)Enum.Parse(typeof (TValue), value, true); - Paulo Santos

3

흥미롭게도, 이것은 분명히다른 언어에서 가능하다.(Managed C ++, IL 직접).

인용하기 :

... 두 제약 조건은 실제로 유효한 IL을 생성하며 다른 언어로 작성된 경우 C #에서 소비 할 수도 있습니다 (관리되는 C ++ 또는 IL에서 이러한 제약 조건을 선언 할 수 있음).

누가 알아


  • C ++ 용 Managed Extensions은 제네릭에 대한 지원이 없기 때문에 C ++ / CLI를 의미한다고 생각합니다. - Ben Voigt

3

이것은 그것에 나의 포획이다. 답변과 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 소스


  • 이것은 실제로 이해가되지 않습니다. 만약TEnum실제로는 Enum 유형이지만text빈 문자열입니다.ArgumentException& quot; TEnum은 열거 형이어야합니다 & quot; 비록 그것이 그렇다고해도. - Nick

3

기존 답변은 C # ≤ 7.2에서 true입니다. 그러나 C # 언어가 있습니다.기능 요청(에 묶여corefx기능 요청)을 허용합니다;

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

글을 쓰는 시점에서이 기능은 언어 개발 회의 (Language Development Meetings)에서 "토론 중"입니다.

편집하다

Nawfal님의 정보, 이것은 C #7.3.


  • 거기 흥미로운 토론, 감사합니다. 아무것도 아직 돌로 설정 (아직) - johnc
  • @ johnc, 매우 사실이지만 메모와 그것의 가치~이다.자주 묻는 기능. 그것에 공정한 확율은 들어오고 있습니다. - DiskJunky
  • 이것은 C # 7.3에서 나오고 있습니다 :docs.microsoft.com/en-us/visualstudio/releasenotes/…. :) - nawfal

1

나는 항상 이것을 좋아했다 (당신은 적절하게 수정할 수 있었다) :

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


1

일리노이를 사용하는 Christopher Currens의 솔루션을 좋아했지만 MSIL을 자신의 빌드 프로세스에 포함시키는 까다로운 비즈니스를 처리하고 싶지 않은 사람들을 위해 C #에서 비슷한 기능을 썼습니다.

그래도 일반적인 제한을 사용할 수는 없지만where T : EnumEnum은 특별한 타입이기 때문에. 따라서 주어진 generic 형식이 실제로 enum인지 확인해야합니다.

내 기능은 다음과 같습니다.

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


1

Vivek의 솔루션을 재사용 할 수있는 유틸리티 클래스에 캡슐화했습니다. 귀하의 유형에 "T : struct, IConvertible"형식 제약 조건을 정의해야합니다.

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


1

확장 메서드를 만들었습니다.to get integer value from enum메소드 구현을 살펴 본다.

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

이것은 사용법이다.

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


  • 아마 작동하는 동안, 그것은 그 질문에 거의 관련이 없습니다. - quetzalcoatl

1

이전에 다른 답변에서 언급 한 것처럼 이것은 소스 코드로 표현 될 수 없지만 IL 레벨에서 실제로 수행 될 수 있습니다. @Christopher Currens대답일리노이가 어떻게하는지 보여줍니다.

사료s Add-InExtraConstraints.Fody이를 달성하기 위해 빌드 툴링으로 완성되는 아주 간단한 방법이 있습니다. 그냥 너겟 패키지를 추가하십시오 (Fody,ExtraConstraints.Fody)를 프로젝트에 추가하고 다음과 같이 제약 조건을 추가하십시오 (ExtraConstraints의 Readme에서 발췌 한 내용).

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

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

Fody는 제약이 존재하기 위해 필요한 일리노이를 추가 할 것입니다. 또한 대리자를 제한하는 추가 기능에 유의하십시오.

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

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

Enum에 관해서는 매우 흥미로운 것을 메모하고 싶을 수도 있습니다.Enums.NET.


0

나중에 직접 다이렉트 캐스팅을 사용해도 괜찮다면System.Enum필요한 경우 기본 메소드를 사용하십시오. 형식 매개 변수를 신중하게 바꾸면됩니다. 따라서 메소드 구현은 다음과 같습니다.

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

다음과 같이 사용할 수 있습니다.

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


  • 사용Enum.ToObject()보다 유연한 결과를 얻을 수 있습니다. 추가 할 필요가없는 대소 문자 구분없이 문자열 비교를 수행 할 수 있습니다.ToLower() - DiskJunky

-5

자바에서는 다음을 사용합니다.

    SomeClass<T extends enum> {
}

아주 간단합니다.

연결된 질문


최근 질문