Search code examples
c#reflectionexpression-trees

Passing a value using Expression Trees does not work


I am working on a function that takes a delegate type and a delegate of type Action<Object[]> and creates a dynamic function of the given type, that passes, if called, all arguments to the given action handle:

    public static T GetEventDelegate<T>(Action<Object[]> handler) where T : class
    {
        if (!typeof(T).IsSubclassOf(typeof(Delegate)))
            throw new Exception("T must be a delegate type");

        Type[] argTypes = typeof(T).GetMethod("Invoke").GetParameters().Select((para) => para.ParameterType).ToArray();

        List<ParameterExpression> lstArgs = new List<ParameterExpression>(
            argTypes.Select((arg)=>Expression.Parameter(arg))    
        );

        ParameterExpression result = Expression.Variable(typeof(Object[]));

        var assignExpression = Expression.NewArrayInit(typeof(Object),lstArgs.ToArray());

        var callExpression = Expression.Call(handler.Method, result);

        var block = Expression.Block(
            new List<ParameterExpression>(){result},
            new Expression[]{assignExpression,callExpression}
        );

        var del = Expression.Lambda(block, lstArgs.ToArray()).Compile();

        return Delegate.CreateDelegate(typeof(T), del, "Invoke") as T;
    }

    private static void testDel()
    {
        var del = GetEventDelegate<EventHandler>(
            (x) =>
            {
                //Error, because x == null
                Debug.Print(x.ToString());
            }
       );
        del("testString", new EventArgs());
    }

Unfortunately the action handler only gets a null value passed (see testDel()).

Could you please help me to find the error?

Thanks in advance!


Solution

  • Your problem is because you initialize the result ParameterExpression as type Object, but never actually assign it a value. What this pretty much compiles down to (after calling compile) is:

    void Func(arguments..)
    {
       Object result;
       new object[](arguments...);
       method(result);
    }
    

    You never actually assign the array to the result with an assign expression.

    Unrelated to your problem, you could use Expression.Lambda() when creating the lambda because you have the type from the generic.