How to pass runtime argument variable in Expression.Call?

argumentexception c# expression-trees

Question

I'm missing something trivial here. Say I have a method like this:

abstract class C
{
    public static void M(Type t, params int[] i)
    {

    }
}

I'm learning expression trees and I need to build a delegate that calls this method with some predefined arguments. The problem is that I don't know to choose the correct overload and pass arguments of Expression.Call.

I want to achieve this:

//I have other overloads for M, hence I need to specify the type of arugments
var methodInfo = typeof(C).GetMethod("M", new Type[] { typeof(Type), typeof(int[]) });

//this is the first argument to method M, not sure if I have chosen the right expression
var typeArgumentExp = Expression.Parameter(someType);

var intArrayArgumentExp = Enumerable.Repeat(Expression.Constant(0), 3);

var combinedArgumentsExp = new Expression[] { typeArgumentExp }.Concat(intArrayArgumentExp);
var call = Expression.Call(methodInfo, combinedArgumentsExp);

At the Expression.Call line I get:

An unhandled exception of type 'System.ArgumentException' occurred in System.Core.dll

Additional information: Incorrect number of arguments supplied for call to method 'Void M(System.Type, Int32[])'

Where have I gone wrong?

Accepted Answer

I solved it like this (shown here Calling (params object[]) with Expression[]):

//I have other overloads for M, hence I need to specify the type of arugments
var methodInfo = typeof(C).GetMethod("M", new Type[] { typeof(Type), typeof(int[]) });

//I fixed this issue where the first argument should be typeof(Type)
var typeArgumentExp = Expression.Parameter(typeof(Type));

var intArrayArgumentExp = Expression.NewArrayInit(typeof(int), Enumerable.Repeat(Expression.Constant(0), 3));

var combinedArgumentsExp = new Expression[] { typeArgumentExp }.Concat(intArrayArgumentExp);
var call = Expression.Call(methodInfo, combinedArgumentsExp);

Expression.NewArrayInit does the trick. Thanks hvd for his direction.


Popular Answer

The params keyword does not do anything at runtime. When you call C.M(t, 1, 2, 3), the compiler transforms this to C.M(t, new int[] { 1, 2, 3 }). In this case, you are performing parts of the job of the compiler, and this is one transformation that becomes your responsibility. You should explicitly create the array, and call C.M with exactly two arguments.



Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why