O desempenho importa
Se você quer um melhor desempenho, este é o caminho a seguir:
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");
}
}
}
Por que isso tem melhor desempenho?
Como todos os métodos internos usam código muito semelhante a esse, exceto que também executam vários outros códigos com os quais não nos importamos . O código Enum do C # é bastante horrível em geral.
O código acima foi Linq-ified e simplificado, portanto, ele contém apenas os bits com os quais nos preocupamos.
Por que o código interno é lento?
Primeiro sobre Enum.ToString () -vs- Enum.GetName (..)
Sempre use o último. (Ou melhor ainda, como ficará claro abaixo.)
ToString () usa o último internamente, mas, novamente, também faz um monte de outras coisas que não queremos, por exemplo, tenta combinar sinalizadores, imprimir números etc. Estamos interessados apenas nas constantes definidas dentro do enum.
Por sua vez, Enum.GetName obtém todos os campos, cria uma matriz de string para todos os nomes, usa o ToUInt64 acima em todos os seus RawConstantValues para criar uma matriz UInt64 de todos os valores, classifica as duas matrizes de acordo com o valor UInt64 e, finalmente, obtém o nome de a matriz de nomes fazendo um BinarySearch na matriz UInt64 para encontrar o índice do valor que desejávamos.
... e depois jogamos os campos e as matrizes classificadas para longe, use esse nome para encontrar o campo novamente.
Uma palavra: "Ugh!"