mercredi 26 août 2020

C# - Creating lambda functions using expression trees of an arbitrary delegate type

I am trying to create a runtime lambda function of an arbitrary type, that collects the arguments, that were supplied to it, in a list of objects and passes them to another method of type void Method(List<object> list) to process them. I wrote this code, but got really confused with the result:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace LambdaTest
{
    class LambdaCreator
    {
        ParameterExpression[] Parameters;
        int Index = 0;
        public ParameterExpression Next() 
        {
            return Parameters[Index++];
        }
        public void ResetIndex() 
        {
            Index = 0;
        }

        public void Bar(List<object> parameters)
        {
            foreach (var p in parameters)
            {
                PrintType(p);
            }
        }

        public void PrintType(object arg) 
        {
            Console.WriteLine(arg.GetType().Name);
        }

        public T CreateLambda<T>() where T : class
        {
            var barMethod = GetType().GetMethod("Bar");

            Parameters = typeof(T).GetMethod("Invoke")
                .GetParameters()
                .Select(x => Expression.Parameter(x.ParameterType))
                .ToArray();

            var parametersCount = Expression.Constant(Parameters.Length);

            var listType = typeof(List<object>);
            var list = Expression.Variable(listType);
            var index = Expression.Variable(typeof(int));

            var thisObject = Expression.Constant(this);

            var resetIndex = GetType().GetMethod("ResetIndex");
            var next = GetType().GetMethod("Next");
            var printType = GetType().GetMethod("PrintType");

            var add = listType.GetMethod("Add");

            var breakLabel = Expression.Label();

            var block = Expression.Block(
                new ParameterExpression[] { list, index },
                Expression.Call(thisObject, printType, Parameters.FirstOrDefault()),
                Expression.Call(thisObject, resetIndex),
                Expression.Assign(list, Expression.New(listType)),
                Expression.Loop(
                    Expression.Block(
                        Expression.IfThen(Expression.GreaterThanOrEqual(index, parametersCount), Expression.Break(breakLabel)),
                        Expression.Call(list, add, Expression.Call(thisObject, next)),
                        Expression.AddAssign(index, Expression.Constant(1))
                    ),
                    breakLabel
                ),
                Expression.Call(thisObject, barMethod, list)
            );
            
            var lambda = Expression.Lambda(typeof(T), block, Parameters);
            var compiledLambda = lambda.Compile() as T;
            return compiledLambda;
        }
    }
    class Program
    {
        delegate void Foo(string a, int b);

        static void Main(string[] args)
        {
            var test = new LambdaCreator();
            var l = test.CreateLambda<Foo>();
            l("one", 2);
        }
    }
}

The output of the program was:

String
PrimitiveParameterExpression`1
PrimitiveParameterExpression`1

I was expecting to get:

String
String
Int32

Somehow I lose the values of the arguments when I put them in a list and pass it to the Bar method. Can someone tell me where is the problem I how can I fix it. Or is there another way to collect the arguments and pass them through? I'm really new to this expression trees stuff. Thanks in advance!





Aucun commentaire:

Enregistrer un commentaire