Выберите правильный общий метод с отражением - PullRequest
32 голосов
/ 03 сентября 2010

Я хочу выбрать правильный обобщенный метод с помощью отражения и затем вызвать его.

Обычно это довольно просто.Например,

var method = typeof(MyType).GetMethod("TheMethod");
var typedMethod = method.MakeGenericMethod(theTypeToInstantiate);

Однако проблема возникает, когда существуют различные общие перегрузки метода.Например, статические методы в классе System.Linq.Queryable.Существует два определения метода «Где»

static IQueryable<T> Where(this IQueryable<T> source, Expression<Func<T,bool>> predicate)
static IQueryable<T> Where(this IQueryable<T> source, Expression<Func<T,int,bool>> predicate)

Это означает, что GetMethod не работает, потому что он не может их описать.Поэтому я хочу выбрать правильный.

До сих пор я часто использовал первый или второй метод, в зависимости от моих потребностей.Вот так:

var method = typeof (Queryable).GetMethods().First(m => m.Name == "Where");
var typedMethod = method.MakeGenericMethod(theTypeToInstantiate);

Однако я не доволен этим, потому что я делаю огромное предположение, что первый метод - правильный.Я скорее хочу найти правильный метод по типу аргумента.Но я не мог понять, как.

Я попробовал это с передачей 'типов', но это не сработало.

        var method = typeof (Queryable).GetMethod(
            "Where", BindingFlags.Static,
            null,
            new Type[] {typeof (IQueryable<T>), typeof (Expression<Func<T, bool>>)},
            null);

Так что кто-нибудь знает, как мне найти«правильный» универсальный метод через рефлексию.Например, правильная версия метода Where в Queryable-классе?

Ответы [ 12 ]

0 голосов
/ 11 февраля 2015

Ответ Антамира был очень полезен для меня, но в нем есть ошибка, заключающаяся в том, что он не проверяет, соответствует ли число параметров в найденном методе количеству типов, передаваемых при предоставлении комбинации общих и конкретных типов.

Например, если вы запустили:

type.GetMethod("MyMethod",typeof(Refl.T1),typeof(bool))

, он не может различить два метода:

MyMethod<T>(T arg1)
MyMethod<T>(T arg1, bool arg2)

Два вызова:

var p = method.GetParameters();   

следует изменить на:

var p = method.GetParameters();   
if (p.Length != parameters.Length)
{
    correct = false;
    continue;
}

Кроме того, обе существующие строки «разрыв» должны быть «продолжить».

0 голосов
/ 04 мая 2014

Ответ Криса Москини хорош, если вы знаете имя метода во время компиляции. Ответ Антамира сработает, если мы получим имя метода во время выполнения, но это слишком излишне.

Я использую другой способ, для которого я получил вдохновение, используя отражатель из функции .NET Expression.Call, который выбирает правильный обобщенный метод из строки.

public static MethodInfo GetGenericMethod(Type declaringType, string methodName, Type[] typeArgs, params Type[] argTypes) {
    foreach (var m in from m in declaringType.GetMethods()
                        where m.Name == methodName
                            && typeArgs.Length == m.GetGenericArguments().Length
                            && argTypes.Length == m.GetParameters().Length
                        select m.MakeGenericMethod(typeArgs)) {
        if (m.GetParameters().Select((p, i) => p.ParameterType == argTypes[i]).All(x => x == true))
            return m;
    }

    return null;
}

Использование:

var m = ReflectionUtils.GetGenericMethod(typeof(Queryable), "Where", new[] { typeof(Person) }, typeof(IQueryable<Person>), typeof(Expression<Func<Person, bool>>));

Если вам нужно только определение универсального метода или вы просто не знаете тип T, вы можете использовать некоторые фиктивные типы и затем удалить информацию универсального:

var m = ReflectionUtils.GetGenericMethod(typeof(Queryable), "Where", new[] { typeof(object) }, typeof(IQueryable<object>), typeof(Expression<Func<object, bool>>));
m = m.GetGenericMethodDefinition();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...