यह सुविधा अंत में 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;
}
7.3 के संस्करण में अपने C # प्रोजेक्ट में अपना भाषा संस्करण सेट करना सुनिश्चित करें।
नीचे मूल उत्तर:
मुझे खेल में देर हो गई है, लेकिन मैंने इसे चुनौती के रूप में लिया कि यह कैसे हो सकता है। यह 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
आपको असेंबली के बजाय नेटमॉडल मिलता है।
दुर्भाग्य से, VS2010 (और पहले, जाहिर है) नेटमॉडल संदर्भों को जोड़ने का समर्थन नहीं करता है, जिसका अर्थ है कि आपको डिबगिंग करते समय इसे 2 अलग-अलग विधानसभाओं में छोड़ना होगा। एकमात्र तरीका है कि आप उन्हें अपनी विधानसभा के भाग के रूप में जोड़ सकते हैं /addmodule:{files}
कमांड लाइन तर्क का उपयोग करके खुद को csc.exe चलाना होगा । यह MSBuild स्क्रिप्ट में बहुत दर्दनाक नहीं होगा । बेशक, यदि आप बहादुर या मूर्ख हैं, तो आप हर बार स्वयं सीबीएस चला सकते हैं। और यह निश्चित रूप से अधिक जटिल हो जाता है क्योंकि कई विधानसभाओं को इसकी पहुंच की आवश्यकता होती है।
तो, यह .Net में किया जा सकता है। क्या यह अतिरिक्त प्रयास के लायक है? उम, ठीक है, मुझे लगता है कि मैं आपको उस पर फैसला करने दूँगा।
विकल्प के रूप में एफ # समाधान
अतिरिक्त क्रेडिट: यह पता चला है कि 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
यह बनाए रखने के लिए आसान है क्योंकि यह पूर्ण विज़ुअल स्टूडियो आईडीई समर्थन के साथ एक प्रसिद्ध भाषा है, लेकिन आपको अभी भी इसके समाधान में एक अलग परियोजना की आवश्यकता है। हालांकि, यह स्वाभाविक रूप से काफी अलग आईएल (कोड का उत्पादन है बहुत अलग) है और यह पर निर्भर करता है FSharp.Core
पुस्तकालय है, जो, बस किसी भी अन्य बाहरी पुस्तकालय की तरह, अपने वितरण का हिस्सा बनने के लिए की जरूरत है।
यहां बताया गया है कि आप इसका उपयोग कैसे कर सकते हैं (मूल रूप से एमएसआईएल समाधान के समान), और यह दिखाने के लिए कि यह अन्यथा पर्यायवाची मॉडल पर सही ढंग से विफल रहता है:
// 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);