3

I want to write a method that will analyze custom attributes of any method (with any number of arguments and any return type) knowing only method info. This function will check if method has specific Attribute. like this: var tmp = methodInfo.GetCustomAttributes(typeof(LineItemAttribute),false); and if it has such attribute It will execute it.And I want to make call of that function really easy to use. So, in example there are three methods and method GetMethodAttributes that I want to call.

class Test
{
      public static void Main()
      {
      }

      public void Test1(){}

      public void Test2(int a){}

      public void Test3(object a, string c, Boolean d);

      public void GetMethodAttributes(MethodInfo mi) {}
}

Ideally I want to write something like that

public static void Main()
    {
        var t = new Test();
        GetMethodAttributes(t.Test1);
        GetMethodAttributes(t.Test2);
        GetMethodAttributes(t.Test3);
    }

I don't want to use string representation of the method names as method names may change, like that:

MethodInfo info = type.GetMethod(name);

Do I have any options? Basically I need a way to use delegates for functions with different sinatures

vmg
  • 9,920
  • 13
  • 61
  • 90
  • What is `GetMethodAttributes` doing it return nothing, what are you expecting `GetMethodAttributes(t.Test1);` to do exactly? – sa_ddam213 Jan 15 '13 at 00:59
  • This function will check if method has specific Attribute. like this: *var tmp = methodInfo.GetCustomAttributes(typeof(LineItemAttribute),false);* and if it has such attribute It will execute it. – vmg Jan 15 '13 at 01:02
  • Lets say `public void Test3(object a, string c, Boolean d);` has the attribute, how are you going to execute withou any parameters passed in – sa_ddam213 Jan 15 '13 at 01:06
  • ha :) I'm doing quick and dirty implementation of the data driven test runner and I will use `Microsoft.Test.DataDriven.LineItemAttribute` to specify arguments. Something like: `[LineItem(null, string.empty, false)] [LineItem(null, string.empty, true)] public void Test3(object a, string c, Boolean d);` – vmg Jan 15 '13 at 01:11
  • 2
    In Eric Lippert's blog: [In Foof We Trust: A Dialogue](http://blogs.msdn.com/b/ericlippert/archive/2009/05/21/in-foof-we-trust-a-dialogue.aspx) – horgh Jan 15 '13 at 01:19
  • 1
    There's a pretty neat way of doing this using implicit operators, even works well for overload resolution: http://stackoverflow.com/a/3472250/1269654 EDIT: The sample there uses static methods, but it works just fine for instance methods too (don't even need an instantiated instance, just a compile-time reference to a typed null value will do fine) – Chris Sinclair Jan 15 '13 at 02:10
  • possible duplicate of [get methodinfo from a method reference C#](http://stackoverflow.com/questions/9382216/get-methodinfo-from-a-method-reference-c-sharp) – nawfal Apr 27 '13 at 02:04

2 Answers2

4

As Chris Sinclair pointed out in the comment above; you can use a delegate without using reflection or expression trees to get the MethodInfo. The downside is that the compiler is not able to infer the generic parameter so you have to specify the delegate type to match the signature of the given method like this:

public class Test
{
    public static void Main()
    {
        var t = new Test();
        CheckMethodAttributes<Action>(t.Test1);
        CheckMethodAttributes<Action<int>>(t.Test2);
        CheckMethodAttributes<Action<object, string, bool>>(t.Test3);
    }

    public void Test1() { }

    public void Test2(int a) { }

    public void Test3(object a, string c, bool d) { }

    public static void CheckMethodAttributes<T>(T func)
    {
        MethodInfo method = new MethodOf<T>(func);

        // Example attribute check:
        var ignoreAttribute = method.GetAttribute<IgnoreAttribute>();
        if (ignoreAttribute != null)
        {
            // Do something here...
        }
    }
}

This uses two utility classes, the MethodOf<T> for extracting the MethodInfo from the given Delegate and some AttributeUtils to get strongly typed custom attribute retrieval:

public static class AttributeUtils
{
    public static bool HasAttribute<TAttribute>(this MemberInfo member, bool inherit = true)
        where TAttribute : Attribute
    {
        return member.IsDefined(typeof(TAttribute), inherit);
    }

    public static TAttribute GetAttribute<TAttribute>(this MemberInfo member, bool inherit = true)
        where TAttribute : Attribute
    {
        return member.GetAttributes<TAttribute>(inherit).FirstOrDefault();
    }

    public static IEnumerable<TAttribute> GetAttributes<TAttribute>(this MemberInfo member, bool inherit = true)
        where TAttribute : Attribute
    {
        return member.GetCustomAttributes(typeof(TAttribute), inherit).Cast<TAttribute>();
    }
}

public class MethodOf<T>
{
    public MethodOf(T func)
    {
        var del = func as Delegate;
        if (del == null) throw new ArgumentException("Cannot convert func to Delegate.", "func");

        Method = del.Method;
    }

    private MethodInfo Method { get; set; }

    public static implicit operator MethodOf<T>(T func)
    {
        return new MethodOf<T>(func);
    }

    public static implicit operator MethodInfo(MethodOf<T> methodOf)
    {
        return methodOf.Method;
    }
}
Community
  • 1
  • 1
khellang
  • 17,550
  • 6
  • 64
  • 84
  • I implemented similar approach: Call `DataDrivenTestRunner.RunTheMethod(new Action(this.BopRsSingleColumnOneItemIncreasedFontDataDriven));` Method signature: `public static void RunTheMethod(Delegate delegateToRun)` And getting method info: `MethodInfo mi = delegateToRun.Method;` – vmg Jan 15 '13 at 18:52
2

You can do something like this using Expression Trees, where you pass the method via a lambda expression. You do still need to pass stub values for the parameters, however. For a good example of this in action, check out the source code for Moq, which uses this pattern extensively for setting up mock behaviors for unit testing. Just note that this is not a trivial thing to set up. If you need something relatively quick and dirty, your best bet is probably string names with a good refactoring tool and/or automated tests to help deal with the renaming issues.

Dan Bryant
  • 27,329
  • 4
  • 56
  • 102