Qualcuno conosce un modo rapido per ottenere attributi personalizzati su un valore enum?
-
09-06-2019 - |
Domanda
Probabilmente è meglio mostrarlo con un esempio.Ho un enum con attributi:
public enum MyEnum {
[CustomInfo("This is a custom attrib")]
None = 0,
[CustomInfo("This is another attrib")]
ValueA,
[CustomInfo("This has an extra flag", AllowSomething = true)]
ValueB,
}
Voglio ottenere quegli attributi da un'istanza:
public CustomInfoAttribute GetInfo( MyEnum enumInput ) {
Type typeOfEnum = enumInput.GetType(); //this will be typeof( MyEnum )
//here is the problem, GetField takes a string
// the .ToString() on enums is very slow
FieldInfo fi = typeOfEnum.GetField( enumInput.ToString() );
//get the attribute from the field
return fi.GetCustomAttributes( typeof( CustomInfoAttribute ), false ).
FirstOrDefault() //Linq method to get first or null
as CustomInfoAttribute; //use as operator to convert
}
Poiché si utilizza la riflessione, mi aspetto una certa lentezza, ma sembra complicato convertire il valore enum in una stringa (che riflette il nome) quando ne ho già un'istanza.
Qualcuno ha un modo migliore?
Soluzione
Questo è probabilmente il modo più semplice.
Un modo più rapido sarebbe quello di emettere staticamente il codice IL utilizzando il metodo dinamico e ILGenerator.Anche se l'ho usato solo per GetPropertyInfo, ma non riesco a capire perché non potresti emettere anche CustomAttributeInfo.
Ad esempio il codice per emettere un getter da una proprietà
public delegate object FastPropertyGetHandler(object target);
private static void EmitBoxIfNeeded(ILGenerator ilGenerator, System.Type type)
{
if (type.IsValueType)
{
ilGenerator.Emit(OpCodes.Box, type);
}
}
public static FastPropertyGetHandler GetPropertyGetter(PropertyInfo propInfo)
{
// generates a dynamic method to generate a FastPropertyGetHandler delegate
DynamicMethod dynamicMethod =
new DynamicMethod(
string.Empty,
typeof (object),
new Type[] { typeof (object) },
propInfo.DeclaringType.Module);
ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
// loads the object into the stack
ilGenerator.Emit(OpCodes.Ldarg_0);
// calls the getter
ilGenerator.EmitCall(OpCodes.Callvirt, propInfo.GetGetMethod(), null);
// creates code for handling the return value
EmitBoxIfNeeded(ilGenerator, propInfo.PropertyType);
// returns the value to the caller
ilGenerator.Emit(OpCodes.Ret);
// converts the DynamicMethod to a FastPropertyGetHandler delegate
// to get the property
FastPropertyGetHandler getter =
(FastPropertyGetHandler)
dynamicMethod.CreateDelegate(typeof(FastPropertyGetHandler));
return getter;
}
Altri suggerimenti
In genere trovo che la riflessione sia abbastanza rapida purché non si invochino dinamicamente metodi.
Dato che stai solo leggendo gli attributi di un'enumerazione, il tuo approccio dovrebbe funzionare perfettamente senza alcun reale calo delle prestazioni.
E ricorda che generalmente dovresti cercare di mantenere le cose semplici da capire.Progettarlo troppo solo per guadagnare qualche ms potrebbe non valerne la pena.