Bu özellik sonunda C # 7.3'te desteklenmektedir!
Aşağıdaki kod parçası ( dotnet örneklerinden ) nasıl olduğunu gösterir:
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 # projenizde dil sürümünüzü 7.3 sürümüne ayarladığınızdan emin olun.
Aşağıdaki orijinal cevap:
Oyuna geç kaldım, ama nasıl yapılabileceğini görmek için bir meydan okuma olarak aldım. C # (veya VB.NET'te mümkün değildir, ancak F # için aşağı kaydırın), ancak MSIL'de mümkündür . Bu küçük şeyi yazdım.
// 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
}
}
Hangi bir işlev oluşturur ediyorum geçerli C # olsaydı, şu şekilde görünür:
T GetEnumFromString<T>(string valueString, T defaultValue) where T : Enum
Sonra aşağıdaki C # kodu ile:
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
}
Ne yazık ki, bu, kodunuzun bu bölümünün C # yerine MSIL'de yazılması anlamına gelir; tek ek avantajı, bu yöntemi kısıtlayabilmenizdir System.Enum
. Aynı zamanda bir serseri, çünkü ayrı bir montajda derlenir. Ancak, bu şekilde dağıtmanız gerektiği anlamına gelmez.
Çizgiyi kaldırarak .assembly MyThing{}
ve ilasmı aşağıdaki gibi çağırarak:
ilasm.exe /DLL /OUTPUT=MyThing.netmodule
montaj yerine bir netmodül elde edersiniz.
Ne yazık ki, VS2010 (ve daha önceki sürümlerde) netmodule referanslarının eklenmesini desteklemez, yani hata ayıklama sırasında 2 ayrı derlemede bırakmanız gerekir. Bunları derlemenizin bir parçası olarak eklemenin tek yolu, /addmodule:{files}
komut satırı bağımsız değişkenini kullanarak csc.exe dosyasını kendiniz çalıştırmak olacaktır . Bir MSBuild komut dosyasında çok acı verici olmaz . Tabii ki, cesur veya aptalsanız, csc'yi her seferinde manuel olarak çalıştırabilirsiniz. Ve çoklu montajların buna erişmesi gerektiğinden kesinlikle daha karmaşık hale gelir.
Yani, .Net'te yapılabilir. Ekstra çabaya değer mi? Şey, sanırım buna karar vermene izin vereceğim.
F # Alternatif olarak çözüm
Ekstra Kredi: enum
MSIL: F # dışında en az bir .NET dilinde genel bir kısıtlamanın mümkün olduğu ortaya çıkıyor .
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
Tam Visual Studio IDE desteği ile iyi bilinen bir dil olduğu için bu dilin bakımı daha kolaydır, ancak yine de bunun için çözümünüzde ayrı bir projeye ihtiyacınız vardır. Bununla birlikte, doğal olarak ürettiği ölçüde farklı OL (kod olan çok farklı) ve dayanırFSharp.Core
tıpkı diğer dış kitaplığı gibi Dağıtımınız bir parçası haline gerekiyor, kütüphane,.
Bunu (temel olarak MSIL çözümüyle aynı şekilde) nasıl kullanabileceğiniz ve başka türlü eşanlamlı yapılarda doğru şekilde başarısız olduğunu göstermek için:
// 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);