如何将表达式<Action<T, object>> 转换为表达式<Action<T, U>>?

How to convert Expression<Action<T, object>> to Expression<Action<T, U>>?

我有一个接受 object 类型参数的表达式。我需要使用 Type 变量在 运行 时间内创建一个类型化表达式。见下文。参数应该是 int 类型,这样我最终得到一个 Expression<Action<Program, int>>,对于给定的 int 调用非类型化的 Set 方法。如何做到这一点?

class Program
{
    private static Type SomeRuntimeType() => typeof(int);

    public void Set(object v)
    {
        Debug.WriteLine("Setting value...");
    }

    static void Main(string[] args)
    {
        Expression<Action<Program, object>> e1 = (t, v) => t.Set(v);

        var type = SomeRuntimeType();
        // TODO: Create typed expression...
        Expression<Action<Program, type>> e2 = ...
    }
}

简单的解决方案:从 C# 的角度来看,Lambda 表达式是无类型的(它的类型是 LambdaExpression)。在运行时它属于 "right type"(如 Expression<Action<Program, T2>>,这是可能的,因为 Expression<T> 子类 LambdaExpression

Expression<Action<Program, object>> e1 = (t, v) => t.Set(v);

var type = typeof(int);
var par1 = e1.Parameters[0];
var par2 = Expression.Parameter(type);

// if type is a value type, you have to expressly box it 
Expression conv = type.IsValueType ? (Expression)Expression.Convert(par2, typeof(object)) : par2;

// We "chain" the two expressions
InvocationExpression invoke = Expression.Invoke(e1, par1, conv);
LambdaExpression lambda = Expression.Lambda(invoke, par1, par2);

var compiled = lambda.Compile();

// sanity check, 
bool lambdaTypeIsExpected = typeof(Expression<>).MakeGenericType(typeof(Action<,>).MakeGenericType(typeof(Program), type)) == lambda.GetType();

请注意,如果某些 ORM 或其他子系统需要 LambdaExpression,并非所有库都支持 Invoke(可以使用表达式重写器删除)或 Convert (这是从值类型转换为引用类型所必需的)。我什至见过不喜欢 object 句号的库(我已经多次尝试过这些技巧 :-) )