2012-06-06 17 views
8

ho bisogno di ottenere MethodInfo per il metodo chiamato in azione delegato al fine di verificare, se i metodi chiamati in azione ha MyCustomAttibutedelegato all'azione. Come ottenere informazioni sui metodi chiamate in delegato?

public void Foo(Action action) 
    { 
     if(Attribute.GetCustomAttributes(action.Method, typeof(MyCustomAttribute)).Count() == 0) 
     { 
      throw new ArgumentException("Invalid action"); 
     } 
    } 

Il metodo Foo dovrebbe essere in grado di essere chiamato come segue:

Foo(() => 
    { 
      instanceOfFooClass.Method1().Method2(); 
    }); 

Nel metodo Foo voglio essere sicuro che Method1 e Method2 abbiano MyCustomAttribute. Comunque action.Method mi sta dando il MethodInfo, che è l'azione del delegato, che avviene quando si usa l'espressione lambda. C'è un modo per ottenere Method1 e Method2 MethodInfo?

+3

Ottima domanda. Non molto facilmente, AFAIK. Potresti * probabilmente * farlo abbastanza facilmente con un 'Expression ', ma poi non puoi * direttamente * eseguirlo. –

+0

Sono d'accordo che dovrai usare gli alberi di espressione per quello. Non so quanto danneggerà le prestazioni. –

+0

Se non direttamente, come è possibile eseguirlo? – Joanna

risposta

5

Come menzionato nei commenti, Expression<T> è probabilmente il modo migliore per raggiungere questo obiettivo. Tuttavia, richiede un Compile() in fase di esecuzione, quindi dovrebbe essere profilato prestazioni.

Con Expression<T> si può facilmente ottenere l'accesso a informazioni metodo come questo:

public MethodInfo GetMethodInfo(Expression<Action> action) 
{ 
    return ((MethodCallExpression)action.Body).Method; 
} 

Ma, prima di eseguire l'azione è necessario farlo:

private void InvokeMethod(Expression<Action> action) 
{ 
    action.Compile().Invoke(); 
} 

EDIT Ah sì, ho Ho dimenticato come ottenere l'accesso all'attributo del cliente. Si potrebbe fare così:

var methodInfo = ((MethodCallExpression)myAction.Body).Method; 
var attributes = methodInfo.GetCustomAttributes<T>(true); 

ESEMPIO Ecco un esempio che mostra il passaggio metodo incatenato chiama a Expression<Action>:

public class ActionTest 
{ 
    public void DoAction(Action action) 
    { 
     action(); 
    } 

    public void DoExpressionAction(Expression<Action> action) 
    { 
     var method2Info = ((MethodCallExpression)action.Body).Method; 

     // a little recursion needed here 
     var method1Info = ((MethodCallExpression)((MethodCallExpression)action.Body).Object).Method; 

     var myattributes2 = method2Info.GetCustomAttributes(typeof(MyAttribute), true); 
     var myattributes1 = method1Info.GetCustomAttributes(typeof(MyAttribute), true); 

     action.Compile().Invoke(); 
    } 
} 

[AttributeUsage(AttributeTargets.Method)] 
public class MyAttribute : Attribute 
{ 
    private string message; 

    public MyAttribute(string message) 
    { 
     this.message = message; 
    } 
} 

public class MethodTest 
{ 
    [MyAttribute("Number1")] 
    public MethodTest Method1() 
    { 
     Console.WriteLine("Action"); 
     return this; 
    } 

    [MyAttribute("Number2")] 
    public MethodTest Method2() 
    { 
     Console.WriteLine("ExpressionAction"); 
     return this; 
    } 
} 


class Program 
{ 
    static void Main(string[] args) 
    { 
     ActionTest target = new ActionTest(); 
     MethodTest instance = new MethodTest(); 

     target.DoExpressionAction(() => instance.Method1().Method2()); 

     Console.ReadLine(); 
    } 

    static void Method1() 
    { 
     Console.WriteLine("Action"); 
    } 

    static void Method2() 
    { 
     Console.WriteLine("ExpressionAction"); 
    } 
} 
+0

Puoi dirmi come trasformare() => {instanceOfFooClass.Method1(). Method2();} in Expression ? – Joanna

+0

Non dovresti doverlo trasformare. Dovresti essere in grado di passarlo direttamente senza modifiche. Fammelo testare. –

+0

Sì, puoi passare '() => action' direttamente a' Expression '. Hai solo bisogno di aggiungere un utilizzo per 'System.Linq.Expressions' –

4

Se si chiama il Foo() Methdod in questo modo:

Foo(instanceOfFooClass.Method); 

Il codice funziona come ci si aspetterebbe (metodi annullati sono azioni, dopotutto). In una nota a margine, penso che le chiamate al metodo "concatenando" contano, mentre passi solo l'ultima.

campione completa dimostrazione del comportamento:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace ConsoleApplication4 
{ 
    class MyCustomAttribute : Attribute { } 
    class FooClass 
    { 
     [MyCustom] 
     public void DecoratedMethod() { Console.WriteLine("Decorated Method - executed."); } 
     public void NotDecoratedMethod() { Console.WriteLine("Not Decoreated Method - executed."); } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      FooClass instanceOfFooClass = new FooClass(); 
      Foo(instanceOfFooClass.DecoratedMethod); 
      Foo(instanceOfFooClass.NotDecoratedMethod); 
      Console.ReadLine(); 
     } 

     public static void Foo(Action action) 
     { 
      if (Attribute.GetCustomAttributes(action.Method, typeof(MyCustomAttribute)).Count() == 0) 
       Console.WriteLine(string.Format("Invalid method {0}", action.Method.Name)); 
      else 
      { 
       Console.WriteLine(string.Format("Valid method {0}", action.Method.Name)); 
       action.Invoke(); 
      } 
     } 
    } 
} 
+0

Non sono sicuro che funzionerà con metodi concatenati. nell'esempio ti ha fornito in qualche modo di analizzare le chiamate interne dell'azione fornita, per verificare sia method1 che method2. a meno che non riscriva il suo codice completamente, questo non funzionerà con semplici controlli dei delegati – YavgenyP

+0

Ovviamente hai ragione, questo probabilmente non funzionerebbe con "catene" di metodi ... Onestamente l'intero concetto mi sembra strano. – Alex