2013-07-30 3 views
3

Предположим, у меня есть класс, как это, содержащий общий метод с из параметров:Как вы называете общий метод без параметров путем отражения?

public class C 
{ 
    public static void M<T>(IEnumerable<T> sequence, out T result) 
    { 
     Console.WriteLine("Test"); 
     result = default(T); 
    } 
} 

От чтения ответы на несколько других вопросов (How to use reflection to call generic Method? и Reflection on a static overloaded method using an out parameter), я думал, что я мог бы быть в состоянии вызвать метод посредством отражения следующим образом:

// get the method 
var types = new[] { typeof(IEnumerable<int>), typeof(int).MakeByRefType() }; 
MethodInfo mi = typeof(C).GetMethod(
    "M", BindingFlags.Static, Type.DefaultBinder, types, null); 

// convert it to a generic method 
MethodInfo generic = mi.MakeGenericMethod(new[] { typeof(int) }); 

// call it 
var parameters = new object[] { new[] { 1 }, null }; 
generic.Invoke(null, parameters); 

Но mi возвращается нуль. Я попытался использовать object вместо int в массиве types, но это тоже не сработает.

Как я могу указать типы (необходимые для параметра out) для общего метода до вызов MakeGenericMethod?

+0

Есть ли у вашего реального класса перегрузки 'M'? Если нет, вы можете просто использовать вариант «GetMethod», где вам не нужно указывать типы параметров. Это не отвечает на вопрос, который вы задали. – hvd

+0

В этом конкретном случае я смогу обойти его, не указывая любые типы и просто используя имя, как предлагалось @SLaks.Мне все еще интересно узнать, что такое синтаксис для указания массива типов шаблонов, или если это невозможно. –

+0

Где я ошибался, я подумал, что необходимо передать массив типов, чтобы использовать параметры 'out' или' ref'. Это не так ... до тех пор, пока у вас есть правильный 'MethodInfo', вы можете передать ему массив параметров, и он установит значения. –

ответ

1

Я все еще интересно знать, что синтаксис для задания массив типов шаблонов, или, если это не представляется возможным

I don't think it's possible to pass that kind of detailed type specification to GetMethod[s]. Я думаю, если у вас есть несколько таких M s, чтобы просмотреть их, вы должны получить их все, а затем фильтровать по различным свойствам MethodInfo s и содержать объекты, например столько, сколько необходимо в вашем конкретном случае:

var myMethodM = 
    // Get all the M methods 
    from mi in typeof(C).GetMethods() 
    where mi.Name == "M" 

    // that are generic with one type parameter 
    where mi.IsGenericMethod 
    where mi.GetGenericArguments().Length == 1 
    let methodTypeParameter = mi.GetGenericArguments()[0] 

    // that have two formal parameters 
    let ps = mi.GetParameters() 
    where ps.Length == 2 

    // the first of which is IEnumerable<the method type parameter> 
    where ps[0].ParameterType.IsGenericType 
    where ps[0].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>) 
    where ps[0].ParameterType.GetGenericArguments()[0] == methodTypeParameter 

    // the second of which is ref <the method type parameter> 
    where ps[1].ParameterType.IsByRef 
    where ps[1].ParameterType.GetElementType() == methodTypeParameter 

    select mi; 
3

Вы прошли параметры, которые найдете M<T>(IEnumerable<int>, ref int).
Вы должны найти M(IEnumerable<T>, ref T) (разница между ref и out существует только на языке C#, а отражение имеет только ref).

Я не уверен, как это передать; вам может понадобиться выполнить все методы, чтобы найти его.

На несвязанной ноте, вам необходимо пройти более BindingFlags:

BindingFlags.Public | BindingFlags.Static 
+0

Непонятно из вашего ответа, но разница между 'ref' и' out' здесь не имеет значения, это должен быть только общий тип. – hvd

+0

@hvd: Да; Я забыл упомянуть об этом. – SLaks

2

Это позволит вам вызвать метод:

MethodInfo mi = typeof(C).GetMethod("M"); 
MethodInfo generic = mi.MakeGenericMethod(new[] { typeof(int) }); 
var parameters = new object[] { new[]{1},null}; 
generic.Invoke(null, parameters); 

И получить из параметра:

Console.WriteLine((int)parameters[1]); //will get you 0(default(int)). 
+0

Обратите внимание, что это не будет работать, если метод перегружен. – SLaks

+0

Да SLaks, я знаю и ваше право, как всегда :), я просто представил это, поскольку OP не упоминал ничего кроме этого единственного метода, я знаю его очень «отличное» решение (lol). – terrybozzio

1

Это хорошо известная проблема; Чтобы найти этот метод, вам нужно знать его параметр типа, но вы не можете узнать его параметр типа, не зная сначала метода ...

Очевидным, но неэлегантным решением является цикл всех методов, пока вы не найдете правильное один.

Другим вариантом является воспользоваться Expression API Linq:

public static MethodInfo GetMethod(Expression<Action> expr) 
{ 
    var methodCall = expr.Body as MethodCallExpression; 
    if (methodCall == null) 
     throw new ArgumentException("Expression body must be a method call expression"); 
    return methodCall.Method; 
} 


... 

int dummy; 
MethodInfo mi = GetMethod(() => C.M<int>(null, out dummy)); 
Смежные вопросы