Вызов функции с использованием отражения, имеющего параметр «params» (MethodBase) - PullRequest
11 голосов
/ 26 июня 2011

У меня есть MethodBases для двух функций:

public static int Add(params int[] parameters) { /* ... */ }
public static int Add(int a, int b) { /* ... */ }

У меня есть функция, которая вызывает MethodBases через класс, который я сделал:

MethodBase Method;
object Target;
public object call(params object[] input)
{
    return Method.Invoke(Target, input);
}

Теперь, если я AddTwoMethod.call(5, 4);, это работаетштраф.

Если я, однако, использую AddMethod.call(5, 4);, он возвращает:

Необработанное исключение: System.Reflection.TargetParameterCountException: параметры не соответствуют подписи

Есть ли способ сделать так, чтобы оба вызова работали нормально без необходимости вручную помещать аргументы в массив для params int[]?

Ответы [ 3 ]

13 голосов
/ 26 июня 2011

Вы можете изменить метод call, чтобы определить параметр params и преобразовать оставшуюся часть ввода в новый массив.Таким образом, ваш метод может действовать почти так же, как логика C #, применяемая к вызову метода.

Что-то, что я быстро сконструировал для вас (знайте, что я тестировал этот метод довольно ограниченным образом, так что может бытьошибки все еще):

public object call(params object[] input)
{
    ParameterInfo[] parameters = Method.GetParameters();
    bool hasParams = false;
    if (parameters.Length > 0)
        hasParams = parameters[parameters.Length - 1].GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0;

    if (hasParams)
    {
        int lastParamPosition = parameters.Length - 1;

        object[] realParams = new object[parameters.Length];
        for (int i = 0; i < lastParamPosition; i++)
            realParams[i] = input[i];

        Type paramsType = parameters[lastParamPosition].ParameterType.GetElementType();
        Array extra = Array.CreateInstance(paramsType, input.Length - lastParamPosition);
        for (int i = 0; i < extra.Length; i++)
            extra.SetValue(input[i + lastParamPosition], i);

        realParams[lastParamPosition] = extra;

        input = realParams;
    }

    return Method.Invoke(Target, input);
}

Имейте в виду, что я тестировал этот метод довольно ограниченным образом, поэтому все еще могут быть ошибки.

6 голосов
/ 26 июня 2011

Предположим, у нас есть следующий пример класса:

public class Test
{
    public static int Add(int i1, int i2)
    {
        return i1 + i2;
    }

    public static int Add(params int[] ints)
    {
        int sum = 0;
        foreach (int i in ints)
            sum += i;

        return sum;
    }
}

Чтобы получить объекты MethodInfo для каждой перегрузки статического метода Add, необходимо выполнить следующее:

 MethodInfo Add2Ints = typeof(Test).GetMethod("Add",  new Type[] { typeof(int), typeof(int) });
 MethodInfo AddParamsInts = typeof(Test).GetMethod("Add", new Type[] { typeof(int[]) });

Чтобы вызвать любой из двух методов, просто передайте аргументы с точным типом, ожидаемым конкретной вызываемой перегрузкой:

 Add2Ints.Invoke(null, new object[] { 1, 2 });
 AddParamsInts.Invoke(null, new object[] { new int[] { 1, 2 } });

Обратите внимание, что следующее не будет работать:

 AddParamsInts.Invoke(null, new object[] { 1, 2 }); 

потому что подпись AddParmsInt на самом деле (int[]) и хотя компилятор, в порядке любезности, позволяет вызывать такой метод как (int, int) под капотом, на самом деле происходит то, что вызов преобразуется для вас на сайте вызовов в эквивалентный (int[]) вызов. В результате отражения у вас нет «справки» компилятора, поэтому вам нужно передать точный тип аргумента, определенный сигнатурой метода.

С учетом всего вышесказанного ваш call метод должен быть следующим:

 public object call(params object[] input)
 {
     return AddParamsInts.Invoke(null /*static*/, new object[] { input.Cast<int>().ToArray() });
 }

Обратите внимание, что вы не можете напрямую привести массив object[] к массиву int[]: int[] ints = (int[])input. Приведение ссылка набрано массивы в значение типа массивы не допускается.

Также важно отметить, что определенные перегрузки метода Add бесполезны, поскольку они перекрываются. Рекомендуется использовать только перегрузку params или, если вы хотите гарантировать, что для оценки сложения требуется как минимум два аргумента, перегрузите их следующим образом:

 public int Add(int i1, int i2) { }
 public int Add(int i1, int i2, params int[] args) { }
4 голосов
/ 26 июня 2011

Вы должны заключить аргументы в массив, но компилятор будет сбит с толку, поэтому вам нужно немного помочь:

Например:

AddMethod.call((object) new int[] {5, 4 });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...