Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

action delegate. How to get methods infos called in delegate?

Tags:

c#

delegates

I need to get MethodInfo for method called in Action delegate in order to check, whether methods called in Action has MyCustomAttibute

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

The Foo method should be able to be called as following:

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

In Foo method I want to be sure that Method1 and Method2 has MyCustomAttribute. However action.Method is giving me the MethodInfo, which is the action of delegate, which happens when using lambda expression. Is there any way to get Method1 and Method2 MethodInfo?

like image 828
Joanna Avatar asked Jun 06 '12 10:06

Joanna


2 Answers

As mentioned in the comments, Expression<T> is probably the best way to achieve this. However, it requires a Compile() at runtime so it should be performance profiled.

With Expression<T> you can easily get access to Method info like this:

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

But, before executing the action you must do this:

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

EDIT Ah yes, I forgot how to get access to the customer attribute. You would do it like this:

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

EXAMPLE Here is an example showing passing chained method calls to 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");
    }
}
like image 65
Davin Tryon Avatar answered Nov 13 '22 07:11

Davin Tryon


If you call your Foo() methdod like this:

Foo(instanceOfFooClass.Method);

Your code works as you'd expect (void methods are actions, after all). On a side note, I think "chaining" method calls in fact counts as you're only passing the last one through.

Full sample demonstrating the behavior:

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();
            }
        }
    }
}
like image 32
Alex Avatar answered Nov 13 '22 08:11

Alex