get methodinfo from a method reference C#
Asked Answered
L

8

37

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.

Lineup answered 21/2, 2012 at 17:30 Comment(7)
Check out the answer I posted to this previous question; https://mcmap.net/q/376625/-how-to-get-array-indexof-lt-string-gt-string-string-methodinfo This might help you with what you're trying to achieve.Erymanthus
@ChrisMcAtackney You may want to post this as an answer, the link has a viable solution.Hoggard
See also Eric Lippert's "In Foof We Trust: A Dialogue" at blogs.msdn.com/b/ericlippert/archive/2009/05/21/…Intermix
I'm curious as to the use case for this.Hibbs
possible duplicate of Why is there not a `fieldof` or `methodof` operator in C#?Compliance
Related: Select Right Generic Method with Reflection.People
Eric Lippert's "In Foof We Trust: A Dialog" link is broken above. It is now learn.microsoft.com/en-us/archive/blogs/ericlippert/…Kavanaugh
E
24

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://mcmap.net/q/376625/-how-to-get-array-indexof-lt-string-gt-string-string-methodinfo

Erymanthus answered 21/2, 2012 at 17:44 Comment(5)
@Jason: It has an overload with no parameters.Erymanthus
@Jason: You've lost me - what's your point? The code posted above does work, and gives exactly what the original question asked for. The MethodInfo of Program.Main. If you want the MethodInfo for the overload which has a string[] parameter, then call it like this; SymbolExtensions.GetMethodInfo(() => Program.Main(null);Erymanthus
Mind you this approach has a corner case (see #6659169). Secondly, its better to check for unary expressions as in cases of boxed conversions like <object> () => IntMethod().Subkingdom
@MatheusRocha If you look at the linked code, you would see you are incorrect. The GetMethodInfo body takes an Expression and unwraps the MethodCallExpression to retrieve the MethodInfo for the method called in the body.Logography
@Logography You're right. I assumed the code would be simpler by using reflection's existing methods. I've deleted my misleading comment.Winter
H
22

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.

Haim answered 6/6, 2019 at 11:6 Comment(3)
Would there be a way to easily pick a generic override if the type is known at runtime only? I haven't found an easy solution and have to manually search methods with GetMethods and manually check its parameters.Rileyrilievo
@Rileyrilievo Do you mean dynamic dispatching ? Because what I am talking about here is purely compile time information. Depending on the problem at hand it could possibly be adapted to a limited extent into a dynamic context. Do you have an example problem?Haim
I needed to be able to dynamically access 2 methods without knowing T at compile time (I was getting a type using reflection). One being Queryable.Where with this signature: public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource>, Expression<Func<TSource, bool>>) and Expression.Lambda<Func<T, bool>> which has 2 overloads with the same parameters, one being generic and one not. public static Expression<TDelegate> Lambda<TDelegate>(Expression, params ParameterExpression[]). For both I had "manually" filter on GetMethods.Rileyrilievo
R
18

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.

Remove answered 21/2, 2012 at 17:48 Comment(1)
Just pass null or default values in place of the parameters.Remove
W
16

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.

Winter answered 6/2, 2017 at 8:8 Comment(0)
H
11

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.

Hamza answered 21/2, 2012 at 17:46 Comment(6)
@Jason - You could get this to work with any number of parameters. Instead of Action use Action<T>, Action<T1, T2>, Action<T1, T2, T3>, or Func<T1>...Cryohydrate
@Jason it does work for Program.Main, see an update for static version. It also works if the method is not an Action, as you can see the method returns void. But in order to use such construct the method should be passed in as an Action. This answers the OP question of how to get method infoHamza
@Jason right I got you, your worry is about parameters, not the static.Hamza
@Jason - In that case use Dmitry's answer. I think oleksii's answer looks cleaner, but Dmitry's is more flexible. But really, most methods could be covered by a dozen overloads, and additional overloads could be added as needed.Cryohydrate
Just did a little profiling. This version is more than 10x faster than expressions. ActionVer Mean: 279.4ns Error: 2.12ns StdDev: 1.66ns ExpressionVer Mean: 3,585.5ns Error: 35.69ns StdDev: 27.86nsWilkie
Course, if speed is a concern, getting it via Reflection's typeof(T).GetMethod(...) was fastest: Mean: 62.46ns Error: 0.886ns StdDev: 0.786nsWilkie
O
4

Maybe not the ideal way, but it could help:

Solution without using lambdas/expressions:

var callback = typeof(BlogController).GetMethod(nameof(BlogController.GetBlogs));
Orator answered 12/12, 2017 at 13:56 Comment(0)
S
1

I created a T4 template that creates the needed helper functions to help you with this. It creates a list of functions to get MethodInfo objects from Func<> or Action<> methods.

Copy the following code into a file named GetMethodInfo.tt:

<#@ template language="C#" #>
<#@ output extension=".cs" encoding="utf-8" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Text" #>
using System;
using System.Linq.Expressions;
using System.Reflection;

namespace Tools
{
    public static class GetMethodInfo
    {
<# int max = 12;
for(int i = 0; i <= max; i++) 
{
    var builder = new StringBuilder();

    for(int j = 0; j <= i; j++) 
    {
        builder.Append("T");
        builder.Append(j);
        if(j != i) 
        {
            builder.Append(", ");
        }
    }

    var T = builder.ToString();
#>
        public static MethodInfo ForFunc<T, <#= T #>>(Expression<Func<T, <#= T #>>> expression)
        {
            var member = expression.Body as MethodCallExpression;

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

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

        public static MethodInfo ForAction<<#= 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");
        }

<# } #>
    }
}

Notes:

  • please make sure, that the Build Action of the .tt template is set to None
  • you can create more or less functions by setting the max variable to the appropriate setting.
Stupidity answered 6/4, 2014 at 16:48 Comment(0)
T
0

Here's a solution based on Linq expressions that works with any number of method parameters:

public static MethodInfo GetMethodInfo<T>(Expression<Func<T, Delegate>> function)
{
    var body = function?.Body as UnaryExpression; // Convert(delegate)
    var operand = body?.Operand as MethodCallExpression; // CreateDelegate(method)
    var @object = operand?.Object as ConstantExpression; // MethodInfo
    var method = @object?.Value as MethodInfo;
    return method;
}

You can then use it like this:

var info = GetMethodInfo<MyClass>(c => c.MyMethod);
Tyr answered 8/12, 2023 at 13:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.