Search code examples
c#expression-trees

Convert Expression<Action<T>> to Expression<Func<T>>


I have Expression<Action<T>> where Action is call of function, but function result is not used. Let's consider the following code sample:

using System;
using System.Linq.Expressions;

namespace ConsoleApp
{
    class Program
    {
        public class MyArg
        {
            public int Data { get; set; }
        }

        public class MyExecutor
        {
            public bool Executed { get; set; }

            public int MyMethod(int simpleArg, MyArg complexArg)
            {
                int result = simpleArg + complexArg.Data;
                this.Executed = true;
                return result;
            }
        }

        static void Main(string[] args)
        {
            Expression<Action<MyExecutor>> expr = t => t.MyMethod(2, new MyArg { Data = 3 });

            var executor = new MyExecutor();
            Action<MyExecutor> action = expr.Compile();
            action(executor);
            Console.WriteLine(executor.Executed); // true
        }
    }
}

There can lot of different actions, with different number of arguments. In all cases I have only such kind of expr which always calls a function and that function always returns the same type, in my example above it is int.

I need to have something like this:

static Expression<Func<MyExecutor, int>> ToExpressionOfFunc(Expression<Action<MyExecutor>> expr)
{
    // TODO
    throw new NotImplementedException();
}

to be able to make a call like this:

    Expression<Func<MyExecutor, int>> funcExpr = ToExpressionOfFunc(expr);
    Func<MyExecutor, int> func = funcExpr.Compile();
    int result = func(executor);
    Console.WriteLine(result); // should print 5

I have a feeling that this should be possible but have no ideas where to start from. I see in debug that there is an expr.Body.Method with desired ReturnType of Int32, but not clear how to extract it properly to the new Expression<Func>.


Solution

  • It's simple, just create a new Expression<Func<MyExecutor, int>> using the body and parameters from the existing expression:

    static Expression<Func<MyExecutor, int>> ToExpressionOfFunc(Expression<Action<MyExecutor>> expr)
    {
        return Expression.Lambda<Func<MyExecutor, int>>(expr.Body, expr.Parameters);
    }
    

    Note that this throws an exception if expr is not of return type int.