2012-06-06 1 views
8

Ich brauche Method für Verfahren in Aktion delegieren, um zu überprüfen, genannt zu bekommen, ob in Aktion aufgerufen Methoden MyCustomAttibuteAktion Delegierter. Wie man Methodeninfos erhält, die im Delegierten genannt werden?

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

Die Foo-Methode sollte in der Lage ist, wie folgt aufgerufen werden:

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

In Foo-Methode möchte ich sicher sein, dass Methode1 und Methode2 MyCustomAttribute hat. Jedoch gibt action.Method mir die MethodInfo, die die Aktion des Delegaten ist, was passiert, wenn Lambda-Ausdruck verwendet wird. Gibt es eine Möglichkeit Method1 und Method2 MethodInfo zu erhalten?

+3

Ausgezeichnete Frage. Nicht sehr leicht, AFAIK. Sie könnten * wahrscheinlich * es einfach mit einem 'Ausdruck ' machen, aber dann können Sie es nicht * direkt * ausführen –

+0

Ich stimme zu, dass Sie Ausdrucksbäume dafür verwenden müssen. Ich weiß nicht, wie sehr es die Leistung beeinträchtigen wird. –

+0

Wenn nicht direkt, wie könnten Sie es dann ausführen? – Joanna

Antwort

5

Wie in den Kommentaren erwähnt, ist Expression<T> wahrscheinlich der beste Weg, dies zu erreichen. Es erfordert jedoch eine zur Laufzeit, so dass es Leistungsprofil sein sollte.

Mit Expression<T> können Sie leicht Zugriff auf Methode info wie diese:

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

Doch bevor die Aktion ausgeführt wird Sie dies tun müssen:

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

EDIT Ah ja, ich habe vergessen, wie man auf das Kundenattribut zugreifen kann. Sie würden es tun, wie folgt:

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

Beispiel Hier ist ein Beispiel vorbei gekettet Verfahren zeigt Anrufe an 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

Können Sie mir sagen, wie man transformiert() => {instanceOfFooClass.Method1(). Method2();} in Ausdruck ? – Joanna

+0

Sie sollten es nicht umwandeln müssen. Sie sollten es direkt ohne Änderungen weitergeben können. Lass mich das testen. –

+0

Yup, Sie können '() => action' direkt an 'Ausdruck ' übergeben. Sie müssen nur eine Verwendung für 'System.Linq.Expressions' hinzufügen –

4

Wenn Sie Ihren Foo() methdod wie folgt aufrufen:

Foo(instanceOfFooClass.Method); 

Ihr Code funktioniert wie erwartet (ungültige Methoden sind Aktionen, immerhin). Nebenbei bemerkt, denke ich, dass das "Verketten" von Methodenaufrufen tatsächlich zählt, da Sie nur den letzten Durchlauf durchführen.

Voll Probe das Verhalten zeigen:

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

Ich bin nicht sicher, ob es mit verketteten Methoden funktioniert. in der Probe, die er Ihnen zur Verfügung gestellt hat, müssen Sie irgendwie die internen Aufrufe der bereitgestellten Aktion analysieren, um Methode1 und Methode2 zu überprüfen. es sei denn, er schreibt seinen Code komplett neu, das funktioniert nicht mit einfachen Checks von Delegierten – YavgenyP

+0

Sie haben natürlich Recht, das würde wahrscheinlich nicht mit "Ketten" von Methoden funktionieren ... Ehrlich gesagt, das ganze Konzept fühlt sich komisch an. – Alex