36

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.

Patrick D'Souza
  • 3,491
  • 2
  • 22
  • 39
Bogdan Verbenets
  • 25,686
  • 13
  • 66
  • 119
  • 1
    Check out the answer I posted to this previous question; http://stackoverflow.com/a/9132588/5827 This might help you with what you're trying to achieve. – Chris McAtackney Feb 21 '12 at 17:34
  • @ChrisMcAtackney You may want to post this as an answer, the link has a viable solution. – Sergey Kalinichenko Feb 21 '12 at 17:37
  • 4
    See also Eric Lippert's "In Foof We Trust: A Dialogue" at http://blogs.msdn.com/b/ericlippert/archive/2009/05/21/in-foof-we-trust-a-dialogue.aspx – phoog Feb 21 '12 at 17:45
  • I'm curious as to the use case for this. – Gabe Feb 21 '12 at 17:53
  • 1
    possible duplicate of [Why is there not a \`fieldof\` or \`methodof\` operator in C#?](http://stackoverflow.com/questions/1213862/why-is-there-not-a-fieldof-or-methodof-operator-in-c) – Sam Harwell May 03 '13 at 02:18
  • Related: [Select Right Generic Method with Reflection](https://stackoverflow.com/q/3631547/1364007). – Wai Ha Lee Sep 05 '19 at 11:29
  • Eric Lippert's "In Foof We Trust: A Dialog" link is broken above. It is now https://learn.microsoft.com/en-us/archive/blogs/ericlippert/in-foof-we-trust-a-dialogue – John Doe Jan 27 '20 at 14:20

7 Answers7

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://stackoverflow.com/a/9132588/5827

Community
  • 1
  • 1
Chris McAtackney
  • 5,192
  • 8
  • 45
  • 69
  • @Jason: It has an overload with no parameters. – Chris McAtackney Feb 22 '12 at 09:35
  • 2
    @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); – Chris McAtackney Feb 22 '12 at 13:57
  • Mind you this approach has a corner case (see http://stackoverflow.com/questions/6658669/lambda-expression-not-returning-expected-memberinfo). Secondly, its better to check for unary expressions as in cases of boxed conversions like ` () => IntMethod()`. – nawfal Dec 13 '13 at 04:59
  • @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. – NetMage Sep 17 '19 at 17:55
  • @NetMage You're right. I assumed the code would be simpler by using reflection's existing methods. I've deleted my misleading comment. – Matheus Rocha Sep 17 '19 at 20:11
21

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.

Frederic
  • 1,580
  • 15
  • 15
  • 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. – jeromej Dec 08 '22 at 09:21
  • @jeromej 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? – Frederic Dec 08 '22 at 22:04
  • 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 Where(this IQueryable, Expression>)` and Expression.Lambda> which has 2 overloads with the same parameters, one being generic and one not. `public static Expression Lambda(Expression, params ParameterExpression[])`. For both I had "manually" filter on GetMethods. – jeromej Dec 12 '22 at 08:58
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.

Dmitry S.
  • 8,373
  • 2
  • 39
  • 49
14

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.

Matheus Rocha
  • 1,152
  • 13
  • 22
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.

oleksii
  • 35,458
  • 16
  • 93
  • 163
  • @Jason - You could get this to work with any number of parameters. Instead of `Action` use `Action`, `Action`, `Action`, or `Func`... – Greg Feb 21 '12 at 18:04
  • @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 info – oleksii Feb 21 '12 at 18:11
  • @Jason right I got you, your worry is about `parameters`, not the `static`. – oleksii Feb 21 '12 at 18:17
  • @Jason - In that case use [Dmitry's answer](http://stackoverflow.com/a/9382501/12971). 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. – Greg Feb 21 '12 at 18:34
  • 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.86ns` – Cory-G Aug 31 '21 at 23:16
  • Course, if speed is a concern, getting it via Reflection's typeof(T).GetMethod(...) was fastest: `Mean: 62.46ns Error: 0.886ns StdDev: 0.786ns` – Cory-G Aug 31 '21 at 23:28
4

Maybe not the ideal way, but it could help:

Solution without using lambdas/expressions:

var callback = typeof(BlogController).GetMethod(nameof(BlogController.GetBlogs));
Mladen B.
  • 2,784
  • 2
  • 23
  • 34
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.
MovGP0
  • 7,267
  • 3
  • 49
  • 42