Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

get methodinfo from a method reference C#

We can use a C# typeof keyword when we want to get Type instance for specified type. But what can I use if I want to get MethodInfo of a method by it's reference?

For example I have a simple console app. It contains Program.Main method. I want to get MethodInfo by using something like methodinfoof(Program.Main). I have this problem because the method names might change, so I cannot just use Type.GetMethodInfo(string MethodName) for that.

I have about 10 000 methods for which I would like to get MethodInfo, so adding any custom attributes or anything else to my methods is not a solution.

like image 444
Bogdan Verbenets Avatar asked Feb 21 '12 17:02

Bogdan Verbenets


5 Answers

Slight adaptation of a previously posted answer, but this blog post seems to achieve what you're asking for; http://blog.functionalfun.net/2009/10/getting-methodinfo-of-generic-method.html

Sample usage would be as follows;

var methodInfo = SymbolExtensions.GetMethodInfo(() => Program.Main());

Original answer was to this question; https://stackoverflow.com/a/9132588/5827

like image 132
Chris McAtackney Avatar answered Nov 14 '22 08:11

Chris McAtackney


You can use expression trees for non-static methods. Here is an example.

using System.Linq.Expressions;
using System.Reflection;

public static class MethodInfoHelper
{
    public static MethodInfo GetMethodInfo<T>(Expression<Action<T>> expression)
    {
        var member = expression.Body as MethodCallExpression;

        if (member != null)
            return member.Method;

        throw new ArgumentException("Expression is not a method", "expression");
    }
}

You would use it like this:

        MethodInfo mi = MethodInfoHelper.GetMethodInfo<Program>(x => x.Test());
        Console.WriteLine(mi.Name);

Test() is a member function declared in the Program class.

Use MemberExpression and MemberInfo instead if you want to support property getters and setters.

like image 29
Dmitry S. Avatar answered Nov 14 '22 09:11

Dmitry S.


Let me add some explanations to the problem at hands here. We are looking for a method GetMethodInfo(SomeMethodSymbol) which returns information about the given method. This is not straitforward because methods may be overloaded in C#. So basically you need to provide additionnal cues into the call to make the compiler (and other code analysers like Intellisense) understand which method you are talking about.

Say for example I am looking for informations about the Math.Abs method. I must then specify which overloaded version of the method I am looking for exactly:

// int
MethodInfo info1 = ((Func<int, int>)Math.Abs).Method;

// or double ?
MethodInfo info2 = ((Func<double, double>)Math.Abs).Method;

Even if there is just one existing overload, like for the Math.Exp method, I must still provide typing cues, because the method might be overloaded in the future and the code would then not compile anymore.

Direct helpers

Given the above remarks, we can provide the following set of helper methods to alleviate somewhat the tedious task of casting every method to reach its informations:

public static class GetMethodInfoUtil
{
    // No cast necessary
    public static MethodInfo GetMethodInfo(Action action) => action.Method;
    public static MethodInfo GetMethodInfo<T>(Action<T> action) => action.Method;
    public static MethodInfo GetMethodInfo<T,U>(Action<T,U> action) => action.Method;
    public static MethodInfo GetMethodInfo<TResult>(Func<TResult> fun) => fun.Method;
    public static MethodInfo GetMethodInfo<T, TResult>(Func<T, TResult> fun) => fun.Method;
    public static MethodInfo GetMethodInfo<T, U, TResult>(Func<T, U, TResult> fun) => fun.Method;

    // Cast necessary
    public static MethodInfo GetMethodInfo(Delegate del) => del.Method;
}

You would then use those helpers like this:

var methodInfos = new[] {

    // Static methods
    GetMethodInfo<int, int>(Math.Abs),
    GetMethodInfo<double, double>(Math.Abs),
    GetMethodInfo<long, long, long>(Math.Max),

    // Static void methods
    GetMethodInfo(Console.Clear),
    GetMethodInfo<string[]>(Main),

    // With explicit cast if too many arguments
    GetMethodInfo((Action<string, object, object>)Console.WriteLine),

    // Instance methods
    GetMethodInfo<string, bool>("".StartsWith),
    GetMethodInfo(new List<int>().Clear),
};

Note that type information should still be provided except for void static method taking no arguments like Console.Clear. Also, for instance methods, an actual instance should be used to get the appropriate method, which uses more resources.

Indirect helpers

Now for some corner cases the above helpers won't work. Say the method uses out parameters for example. In those special cases, extracting method informations from lambda expressions becomes handy, and we get back to the solution provided by other posters (code inspiration from here):

public static class GetIndirectMethodInfoUtil
{
    // Get MethodInfo from Lambda expressions
    public static MethodInfo GetIndirectMethodInfo(Expression<Action> expression) 
        => GetIndirectMethodInfo((LambdaExpression)expression);
    public static MethodInfo GetIndirectMethodInfo<T>(Expression<Action<T>> expression) 
        => GetIndirectMethodInfo((LambdaExpression)expression);
    public static MethodInfo GetIndirectMethodInfo<T, TResult>(Expression<Func<TResult>> expression) 
        => GetIndirectMethodInfo((LambdaExpression)expression);
    public static MethodInfo GetIndirectMethodInfo<T, TResult>(Expression<Func<T, TResult>> expression) 
        => GetIndirectMethodInfo((LambdaExpression)expression);

    // Used by the above
    private static MethodInfo GetIndirectMethodInfo(LambdaExpression expression)
    {
        if (!(expression.Body is MethodCallExpression methodCall))
        {
            throw new ArgumentException(
                $"Invalid Expression ({expression.Body}). Expression should consist of a method call only.");
        }
        return methodCall.Method;
    }
}

You would use those like this:

int dummyInt;
var moreMethodInfos = new[]
{
    // Extracted from lambdas
    GetIndirectMethodInfo(() => "".StartsWith("")),
    GetIndirectMethodInfo((string s) => s.StartsWith(s)),
    GetIndirectMethodInfo(() => int.TryParse("", out dummyInt)),
};

Note that type information is still provided indirectly from argument type. Note as well that a dummy argument has been added just to make it possible to use an out parameter.

Complete demo program: https://dotnetfiddle.net/CkS075.

like image 16
Frederic Avatar answered Nov 14 '22 09:11

Frederic


I know this is a very old post, but I'll just throw this out there for someone who might still be looking for a simple solution to this.. It just seems like no one thought of the simplest solution:

typeof(Program).GetMethods();

Returns an array with the MethodInfo of all methods in the Program class, regardless of attributes or of having parameters or not.

You can iterate ove it if you want, for instance, to list the names of all your 10.000+ methods.

You can also do typeof(Program).GetMethod(nameof(Program.Main)); this way if the method's name change Visual Studio's refactoring will rename it here too.

NOTE: The "nameof" keyword was not available 5 years ago when question was posted.

like image 14
Matheus Rocha Avatar answered Nov 14 '22 10:11

Matheus Rocha


Test class

public class  Foo
{
    public void DoFoo()
    {
        Trace.WriteLine("DoFoo");
    }

    public static void DoStaticFoo()
    {
        Trace.WriteLine("DoStaticFoo");
    }
}

And you can do something like this

MethodInfo GetMethodInfo(Action a)
{
    return a.Method;
}

var foo = new Foo();
MethodInfo mi = GetMethodInfo(foo.DoFoo);
MethodInfo miStatic = GetMethodInfo(Foo.DoStaticFoo);

//do whatever you need with method info

Update
Per @Greg comment if you have some parameters to the methods, you can use Action<T>, Action<T1, T2>, Action<T1, T2, T3>, or Func<T1>, the inconvenience is that you will still need to write the overloads for GetMethodInfo.

like image 11
oleksii Avatar answered Nov 14 '22 09:11

oleksii