2011-01-27 13 views
7

¿Cómo obtengo los atributos personalizados de un método desde Action<T>?¿Cómo obtengo los atributos personalizados de un método de Acción <T>?

Ejemplo:

//simple custom attribute 
public class StatusAttribute : Attribute 
{ 
public StatusAttribute() 
{ 
    Message = ""; 
} 

public string Message { get; set; } 
} 

// an extension methodto wrap MethodInfo.GetCustomAttributes(Type, Bool) with 
// generics for the custom Attribute type 
public static class MethodInfoExtentions 
{ 
public static IEnumerable<TAttribute> GetCustomAttributes<TAttribute>(this MethodInfo methodInfo, bool inherit) 
    where TAttribute : Attribute 
{ 
    object[] attributeObjects = methodInfo.GetCustomAttributes(typeof(TAttribute), inherit); 

    return attributeObjects.Cast<TAttribute>(); 
} 
} 

// test class with a test method to implment the custom attribute 
public class Foo 
{ 
[Status(Message="I'm doing something")] 
public void DoSomething() 
{ 
    // code would go here  
} 
} 

// creates an action and attempts to get the attribute on the action 
private void CallDoSomething() 
{ 
Action<Foo> myAction = new Action<Foo>(m => m.DoSomething()); 
IEnumerable<StatusAttribute> statusAttributes = myAction.Method.GetCustomAttributes<StatusAttribute>(true); 

// Status Attributes count = 0? Why? 
} 

que darse cuenta de que podía hacer esto mediante el uso de la reflexión sobre Foo, pero por lo que estoy tratando de crear tengo que utilizar un Action<T>.

Respuesta

10

El problema es - la acción no apunta directamente al Foo.DoSomething - que apunta a un método generado por el compilador de la forma:

private static void <>__a(Foo m) 
{ 
    m.DoSomething(); 
} 

Una opción en este caso sería cambiar a un Expression<Action<T>>, a continuación, se puede diseccionar el árbol de expresión después y extraer los atributos:

Expression<Action<Foo>> myAction = m => m.DoSomething(); 
var method = ((MethodCallExpression)myAction.Body).Method; 
var statusAttributes = method.GetCustomAttributes<StatusAttribute>(true); 
int count = statusAttributes.Count(); // = 1 
3

la cuestión es que el lambda m => m.DoSomething() es no lo mismo que DoSomething. Es una expresión lambda que se compila en una llamada a método en un método generado por el compilador, posiblemente utilizando un tipo generado por el compilador (aunque tal vez no sea el último, ya que no hay variables locales capturadas).

Una forma muy detallado de conseguir una Action<Foo> de un método de instancia (no estático) del tipo Foo es la siguiente:

var myAction = (Action<Foo>)Delegate.CreateDelegate(
    typeof(Action<Foo>), 
    null, // treat method as static, even though it's not 
    typeof(Foo).GetMethod("DoSomething", BindingFlags.Instance | BindingFlags.Public) 
); 

Obviamente, es decir lejos de ser ideal y, probablemente, de hecho inútil en Tu caso; pero vale la pena conocer;)


actualización: En realidad, sólo se me ocurrió que podría escribir un método de extensión rápida de hacer esto fácil para cualquier método instancia que desee para envolver como un método estático (y mantener la "correcta" MethodInfo):

public static class ActionEx 
{ 
    public static Action<T> ToStaticMethod<T>(this Action action) 
    { 
     if (!(action.Target is T)) 
     { 
      throw new ArgumentException("Blah blah blah."); 
     } 

     return (Action<T>)Delegate.CreateDelegate(
      typeof(Action<T>), 
      null, 
      action.Method 
     ); 
    } 
} 

Esto permitiría que hagas:

Action<Foo> myAction = new Action(new Foo().DoSomething).ToStaticMethod<Foo>(); 

Es cierto que no es tan bueno como m => m.DoSomething(); pero hace darle un Action<T> cuya propiedad Method realmente hace referencia directamente al método DoSomething.


Alternativamente, en lugar de una Action<T>, podría utilizar un Expression<Action<T>> y obtener el MethodInfo de eso. Tenga en cuenta que la sintaxis es la misma que en este caso:

Action<Foo> myAction = m => m.DoSomething(); 
Expression<Action<Foo>> myExpression = m => m.DoSomething(); 

Pero eso es un asunto complicado, ya que un arbitraria Expression<Action<T>> no se garantiza que sea tan simple como m => m.DoSomething().

+0

prefiero ir a la ruta de la sintaxis más limpia, pero ese es un método de extensión impresionante. – mbursill

0

Ninguna de las respuestas anteriores (excepto Marc de la que tiene el número de código del usuario) parece ser compilables :)

Así que yo propondría mía:

private static void CallDoSomething() 
    { 
     var f = new Foo(); 
     Action myAction = f.DoSomething; 
     IEnumerable<StatusAttribute> statusAttributes = myAction.Method.GetCustomAttributes<StatusAttribute>(true); 
    } 
+0

Tienes razón; el mío tuvo un error tonto. Lo "arreglé", aunque el resultado es bastante feo. –

Cuestiones relacionadas