Leistung ist wichtig
Wenn Sie eine bessere Leistung wünschen, ist dies der richtige Weg:
public static class AdvancedEnumExtensions
{
/// <summary>
/// Gets the custom attribute <typeparamref name="T"/> for the enum constant, if such a constant is defined and has such an attribute; otherwise null.
/// </summary>
public static T GetCustomAttribute<T>(this Enum value) where T : Attribute
{
return GetField(value)?.GetCustomAttribute<T>(inherit: false);
}
/// <summary>
/// Gets the FieldInfo for the enum constant, if such a constant is defined; otherwise null.
/// </summary>
public static FieldInfo GetField(this Enum value)
{
ulong u64 = ToUInt64(value);
return value
.GetType()
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
.Where(f => ToUInt64(f.GetRawConstantValue()) == u64)
.FirstOrDefault();
}
/// <summary>
/// Checks if an enum constant is defined for this enum value
/// </summary>
public static bool IsDefined(this Enum value)
{
return GetField(value) != null;
}
/// <summary>
/// Converts the enum value to UInt64
/// </summary>
public static ulong ToUInt64(this Enum value) => ToUInt64((object)value);
private static ulong ToUInt64(object value)
{
switch (Convert.GetTypeCode(value))
{
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
return unchecked((ulong)Convert.ToInt64(value, CultureInfo.InvariantCulture));
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
case TypeCode.Char:
case TypeCode.Boolean:
return Convert.ToUInt64(value, CultureInfo.InvariantCulture);
default: throw new InvalidOperationException("UnknownEnumType");
}
}
}
Warum hat dies eine bessere Leistung?
Da die integrierten Methoden alle Code verwenden, der diesem sehr ähnlich ist, außer dass sie auch eine Reihe anderer Codes ausführen, die uns egal sind . Der Enum-Code von C # ist im Allgemeinen ziemlich schrecklich.
Der obige Code wurde linearisiert und optimiert, sodass er nur die Bits enthält, die uns wichtig sind.
Warum ist der eingebaute Code langsam?
Zunächst zu Enum.ToString () -vs- Enum.GetName (..)
Verwenden Sie immer Letzteres. (Oder noch besser auch nicht, wie weiter unten deutlich wird.)
ToString () verwendet letzteres intern, erledigt aber auch eine Reihe anderer Dinge, die wir nicht wollen, z. B. Versuche, Flags zu kombinieren, Zahlen auszudrucken usw. Wir sind nur an Konstanten interessiert, die in der Aufzählung definiert sind.
Enum.GetName ruft wiederum alle Felder ab, erstellt ein String-Array für alle Namen, verwendet das obige ToUInt64 für alle RawConstantValues, um ein UInt64-Array aller Werte zu erstellen, sortiert beide Arrays nach dem UInt64-Wert und erhält schließlich den Namen von das Name-Array durch Ausführen einer BinarySearch im UInt64-Array, um den Index des gewünschten Werts zu finden.
... und dann werfen wir die Felder weg und die sortierten Arrays verwenden diesen Namen, um das Feld wieder zu finden.
Ein Wort: "Ugh!"