Le prestazioni contano
Se vuoi prestazioni migliori, questa è la strada da percorrere:
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");
}
}
}
Perché questo ha prestazioni migliori?
Poiché i metodi integrati utilizzano tutti un codice molto simile a questo, tranne per il fatto che eseguono anche un sacco di altro codice che non ci interessa . Il codice Enum di C # è abbastanza orribile in generale.
Il codice sopra è stato Linq-ified e ottimizzato, quindi contiene solo i bit che ci interessano.
Perché il codice integrato è lento?
Innanzitutto per quanto riguarda Enum.ToString () -vs- Enum.GetName (..)
Usa sempre quest'ultimo. (O meglio ancora nessuno dei due, come sarà chiaro di seguito.)
ToString () usa quest'ultima internamente, ma di nuovo fa anche un sacco di altre cose che non vogliamo, ad esempio cerca di combinare bandiere, stampare numeri ecc. Siamo interessati solo alle costanti definite all'interno dell'enum.
Enum.GetName a sua volta ottiene tutti i campi, crea un array di stringhe per tutti i nomi, usa il ToUInt64 sopra su tutti i loro RawConstantValues per creare un array UInt64 di tutti i valori, ordina entrambi gli array in base al valore UInt64 e infine ottiene il nome da l'array di nomi eseguendo una ricerca binaria nell'array UInt64 per trovare l'indice del valore desiderato.
... e poi buttiamo via i campi e gli array ordinati usano quel nome per ritrovare il campo.
Una sola parola: "Ugh!"