Вызов функции с использованием отражения с параметром "params" (MethodBase)

У меня есть базы методов для двух функций:

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

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

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[]?

Ответ 1

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

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

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);
}

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

Ответ 2

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

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) { }

Ответ 3

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

Например:

AddMethod.call((object) new int[] {5, 4 });