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.