Вопросы производительности
Если вы хотите лучшую производительность, вот путь:
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");
}
}
}
Почему это имеет лучшую производительность?
Поскольку все встроенные методы используют код, очень похожий на этот, за исключением того, что они также запускают кучу другого кода, который нас не волнует . Код Enum в C # вообще ужасен.
Приведенный выше код был Linq-ified и оптимизирован, поэтому он содержит только те биты, которые нам нужны.
Почему встроенный код работает медленно?
Сначала о Enum.ToString () -vs- Enum.GetName (..)
Всегда используйте последнее. (Или, еще лучше, ни то, ни другое, как станет ясно ниже.)
ToString () использует последнее внутренне, но, опять же, также делает кучу других вещей, которые нам не нужны, например, пытается объединить флаги, распечатать числа и т. Д. Нас интересуют только константы, определенные внутри перечисления.
Enum.GetName, в свою очередь, получает все поля, создает строковый массив для всех имен, использует вышеупомянутый ToUInt64 на всех их RawConstantValues для создания массива всех значений UInt64, сортирует оба массива в соответствии со значением UInt64 и, наконец, получает имя из массив имен, выполнив BinarySearch в массиве UInt64, чтобы найти индекс нужного нам значения.
... а затем мы выбрасываем поля и отсортированные массивы, используя это имя, чтобы снова найти поле.
Одно слово: "Тьфу!"