2012-06-06 10 views
8

Muszę MethodInfo dla metody zwanej w działaniu delegata w celu sprawdzenia, czy metody zwane w działaniu ma MyCustomAttibutedelegat akcji. Jak uzyskać informacje o metodach wywołanych w delegacie?

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

Sposób Foo powinien być w stanie nazwać w następujący sposób:

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

W metodzie Foo chcę mieć pewność, że Method1 i Method2 mają MyCustomAttribute. Jednak action.Method przekazuje mi MethodInfo, która jest działaniem delegata, co dzieje się podczas używania wyrażenia lambda. Czy istnieje sposób, aby uzyskać Method1 i Method2 MethodInfo?

+3

Doskonałe pytanie. Niezbyt łatwo, AFAIK. Mógłbyś * prawdopodobnie * zrobić to z łatwością z "Wyrażeniem ", ale wtedy nie możesz * bezpośrednio * wykonać go –

+0

Zgadzam się, będziesz musiał użyć drzewek wyrażeń do tego. Nie wiem, ile to zaszkodzi wydajności. –

+0

Jeśli nie bezpośrednio, to w jaki sposób można go wykonać? – Joanna

Odpowiedz

5

Jak wspomniano w komentarzach, prawdopodobnie najlepszym sposobem na osiągnięcie tego jest Expression<T>. Jednak wymaga on w środowisku wykonawczym wartości Compile(), dlatego powinien być profilowany pod kątem wydajności.

Z Expression<T> można łatwo uzyskać dostęp do informacji metody takie jak to:

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

Jednak przed wykonaniem czynności należy to zrobić:

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

EDIT Ach tak, Zapomniałem, jak uzyskać dostęp do atrybutu klienta. Można by zrobić to tak:

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

Przykład Oto przykład pokazujący przechodząc metodę łańcuchu połączeń do 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

Czy możesz mi powiedzieć, jak przekształcić() => {instanceOfFooClass.Method1(). Method2();} w Expression ? – Joanna

+0

Nie powinieneś go przekształcać. Powinieneś być w stanie przekazać to bezpośrednio bez żadnych zmian. Po prostu pozwól mi to przetestować. –

+0

Tak, możesz przekazać '() => akcję' prosto do' Wyrażenie '. Trzeba tylko dodać użycie dla 'System.Linq.Expressions' –

4

Jeśli połączyć się Foo() methdod takiego:

Foo(instanceOfFooClass.Method); 

Twój kod działa zgodnie z oczekiwaniami (void method są przecież działaniami). Na marginesie, myślę, że "łańcuchowe" wywołania metodowe w rzeczywistości liczą się, gdy przechodzisz tylko ostatni.

Pełna próba wykazania zachowanie:

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

nie jestem pewien, czy będzie działać z metodami łańcuchowymi. w próbce zapewnił, że musisz przeanalizować wewnętrzne wywołania podanej akcji, aby sprawdzić zarówno metodę 1, jak i metodę2. chyba że całkowicie przepisał swój kod, to nie zadziała z prostymi kontrolami delegatów. – YavgenyP

+0

Masz oczywiście rację, to prawdopodobnie nie działałoby z "łańcuchami" metod ... Szczerze mówiąc, cała koncepcja wydaje mi się dziwna. – Alex

Powiązane problemy