Позвольте мне добавить некоторые объяснения к проблеме здесь.Мы ищем метод GetMethodInfo(SomeMethodSymbol)
, который возвращает информацию о данном методе.Это не просто, потому что методы могут быть перегружены в C #.Таким образом, в основном вам нужно добавить дополнительные вызовы в вызов, чтобы компилятор (и другие анализаторы кода, такие как Intellisense) понимали, о каком методе вы говорите.
Скажем, например, я ищу информацию о методе Math.Abs
.Затем я должен указать, какую именно перегруженную версию метода, которую я ищу, точно:
// int
MethodInfo info1 = ((Func<int, int>)Math.Abs).Method;
// or double ?
MethodInfo info2 = ((Func<double, double>)Math.Abs).Method;
Даже если существует только одна существующая перегрузка, как для метода Math.Exp
, я все равно должен предоставить сигналы ввода, потому чтометод может быть перегружен в будущем, и код больше не будет компилироваться.
Прямые помощники
С учетом приведенных выше замечаний мы можем предоставить следующий набор вспомогательных методов, чтобы несколько облегчить утомительную задачу приведения каждого метода к своей информации:
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;
}
Затем вы можете использовать эти помощники следующим образом:
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),
};
Обратите внимание, что информация о типе все еще должна предоставляться, за исключением метода void static, не принимающего аргументы, такие как Console.Clear
.Кроме того, например, методы, фактический экземпляр должен использоваться для получения соответствующего метода, который использует больше ресурсов.
Косвенные помощники
Теперь для некоторых угловых случаев вышеуказанные помощники не будут работать.Скажем, метод использует out
параметры, например.В этих особых случаях извлечение информации о методах из лямбда-выражений становится удобным, и мы возвращаемся к решению, предоставленному другими авторами (код вдохновения здесь ):
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;
}
}
Вы бы использоваливот такие:
int dummyInt;
var moreMethodInfos = new[]
{
// Extracted from lambdas
GetIndirectMethodInfo(() => "".StartsWith("")),
GetIndirectMethodInfo((string s) => s.StartsWith(s)),
GetIndirectMethodInfo(() => int.TryParse("", out dummyInt)),
};
Обратите внимание, что информация о типе все еще предоставляется косвенно из типа аргумента.Также обратите внимание, что фиктивный аргумент был добавлен только для того, чтобы можно было использовать параметр out
.
Полная демонстрационная программа: https://dotnetfiddle.net/CkS075.