Lars Stenberg
Lars Stenberg

Reputation: 119

Create array of all ExpressionTree/Func-parameters

Output of the following program is:

First: System.String. Second: System.String.

The expected result is: First: hello1. Second: hello2.

If i hardcode the index to 1 or 2 in Expression.Assign(resultArrayAccessor, parameters[0]) it works, but i need to have the index following the variable i.

    public static void Main()
    {
        var type = typeof(Func<string, string, object>);
        var del = GenerateFunc<Func<string, string, object>>(type);
        del("hello1", "hello2");
        Console.ReadLine();
    }

    public static T GenerateFunc<T>(Type type)
    {
        var i = Expression.Parameter(typeof (int), "i");

        var x = type.GetMethod("Invoke");
        var target = typeof (Program).GetMethod("Target");

        var resultArray = Expression.Parameter(typeof(object[]), "result");
        var parameterArray = Expression.Parameter(typeof(ParameterExpression[]), "parameters");

        var resultArrayAccessor = Expression.ArrayAccess(resultArray, i);
        var parameterArrayAccessor = Expression.ArrayAccess(parameterArray, i);

        var label = Expression.Label();

        var parameters = x.GetParameters().Select(p => Expression.Parameter(p.ParameterType, p.ParameterType.ToString())).ToArray();

        var block = Expression.Block(x.ReturnType,
            new[] { resultArray, i, parameterArray },
            Expression.Assign(resultArray, Expression.Constant(new object[parameters.Length])),
            Expression.Assign(parameterArray, Expression.Constant(parameters)),
            Expression.Loop(
                Expression.Block(
                    Expression.IfThenElse(
                        Expression.LessThan(i, Expression.Constant(parameters.Length)),
                            Expression.Block(
                                Expression.Assign(resultArrayAccessor, parameterArrayAccessor),
                                Expression.PostIncrementAssign(i)
                            ),
                        Expression.Break(label)
                    )
                ),
                label
            ),

            Expression.Call(target, resultArray)
        );


        return Expression.Lambda<T>(block, parameters).Compile();
    }

    public static object Target(object[] test)
    {
        Console.WriteLine("First: " + test[0] + ". Second: " + test[1] + ".");
        return null;
    }

Upvotes: 3

Views: 1630

Answers (1)

Lars Stenberg
Lars Stenberg

Reputation: 119

Finally figured it out, much easier syntax aswell as actually working ;)

public static T GenerateFunc<T>(Type type)
{
    var target = typeof (Program).GetMethod("Target");

    var invokeMethod = type.GetMethod("Invoke");
    var parameters = invokeMethod
      .GetParameters()
      .Select(pi => Expression.Parameter(pi.ParameterType, pi.Name))
      .ToList();

    var parametersExpression = Expression.NewArrayInit(typeof(object), parameters);
    var body = Expression.Call(target, parametersExpression);
    return Expression.Lambda<T>(body, parameters).Compile();
}

Upvotes: 3

Related Questions