Wydajność ma znaczenie
Jeśli chcesz uzyskać lepszą wydajność, możesz to zrobić:
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");
}
}
}
Dlaczego ma to lepszą wydajność?
Ponieważ wszystkie wbudowane metody używają kodu bardzo podobnego do tego, z wyjątkiem tego, że uruchamiają także wiele innych kodów, na których nam nie zależy . Kod Enum w C # jest ogólnie dość okropny.
Powyższy kod został opracowany przez Linq i usprawniony, więc zawiera tylko te bity, na których nam zależy.
Dlaczego wbudowany kod działa wolno?
Najpierw dotyczy Enum.ToString () -vs- Enum.GetName (..)
Zawsze używaj tego drugiego. (Lub jeszcze lepiej ani jedno, jak okaże się poniżej.)
ToString () używa tego ostatniego wewnętrznie, ale znowu robi też kilka innych rzeczy, których nie chcemy, np. Próbuje łączyć flagi, drukować liczby itp. Interesują nas tylko stałe zdefiniowane w wyliczeniu.
Enum.GetName z kolei pobiera wszystkie pola, tworzy tablicę ciągów dla wszystkich nazw, używa powyższego ToUInt64 na wszystkich swoich RawConstantValues do utworzenia tablicy UInt64 wszystkich wartości, sortuje obie tablice zgodnie z wartością UInt64, a na końcu pobiera nazwę z tablicę nazw, wykonując BinarySearch w tablicy UInt64, aby znaleźć indeks żądanej wartości.
... a następnie wyrzucamy pola, a posortowane tablice używają tej nazwy, aby ponownie znaleźć pole.
Jedno słowo: „Ugh!”