Calling a function using reflection that has a “pa

2019-01-20 08:00发布

问题:

I have MethodBases for two functions:

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

I have a function that calls the MethodBases via a class I made:

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

Now if I AddTwoMethod.call(5, 4); it works fine.

If I however use AddMethod.call(5, 4); it returns:

Unhandled Exception: System.Reflection.TargetParameterCountException: parameters do not match signature

Is there any way to make it so that both calls work fine without need for manually putting the arguments in an array for the params int[]?

回答1:

You could modify your call method to detect the params parameter and convert the rest of the input to a new array. That way your method could act pretty much the same as the logic C# applies to the method calling.

Something i quicly constructed for you (be aware that i tested this method in a pretty limited way, so there might be errors still):

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

Be aware that i tested this method in a pretty limited way, so there might be errors still.



回答2:

Supposing we have the following example class:

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

To get the MethodInfo objects for each overload of the static Add method you should do the following:

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

In order to invoke any of the two methods, symply pass the arguments with the exact type expected by the specific overload you are invoking:

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

Note that the following will not work:

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

because the signature of AddParmsInt is really (int[]) and although the compiler, as a courtesy, allows you to call such method as (int, int) under the hood what is really happening is that the call is converted for you at the call site to the equivalent (int[]) call. Via reflection you don't have the compiler's "help" so you need to pass the exact argument type defined by the method's signature.

With all that said, your call method should be as follows:

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

Note that you can not directly cast a object[] array to a int[] array: int[] ints = (int[])input. Casting reference typed arrays to value-type arrays is not allowed.

Also important to note is that the defined overloads of the Add method are useless, as they overlap. Consider only using the params overload or, in case you want to guarantee that at least two arguments are needed in order to evaluate an addition, overload them the following way:

 public int Add(int i1, int i2) { }
 public int Add(int i1, int i2, params int[] args) { }


回答3:

You should wrap the arguments in an array, but the compiler will be confused, so you need to help it a bit:

Eg:

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