이 기능은 C # 7.3에서 지원됩니다!
다음 스 니펫 ( dotnet 샘플에서 )은 방법을 보여줍니다.
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.exe /DLL /OUTPUT=MyThing.netmodule
어셈블리 대신 netmodule을 얻습니다.
불행히도 VS2010 (및 이전 버전)은 netmodule 참조 추가를 지원하지 않으므로 디버깅 할 때 2 개의 별도 어셈블리에 남겨 두어야합니다. 어셈블리의 일부로 추가 할 수있는 유일한 방법은 /addmodule:{files}
명령 줄 인수를 사용하여 csc.exe를 직접 실행하는 것 입니다. 너무 아니야MSBuild 스크립트에서는 고통스럽지 . 물론 용감하거나 어리석은 경우 매번 수동으로 csc를 실행할 수 있습니다. 그리고 여러 어셈블리에 액세스해야하기 때문에 확실히 더 복잡해집니다.
따라서 .Net에서 수행 할 수 있습니다. 추가 노력의 가치가 있습니까? 음, 그 결정을 내릴 수있을 것 같아요.
대안으로 F # 솔루션
추가 크레딧 : enum
MSIL 이외의 다른 하나 이상의 .NET 언어에서는 F # 이라는 일반적인 제한 이 가능합니다.
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);