如何转换Expression <action <t,object =“”>&gt;到表达<action <t,u =“”>&gt;?

时间:2018-05-22 11:03:16

标签: c# expression-trees

我有一个接受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 = ...
    }
}

1 个答案:

答案 0 :(得分:2)

简单解决方案:从C#角度来看,Lambda Expression是无类型的(类型为LambdaExpression)。在运行时它是“正确的类型”(如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全程停止的图书馆(我已经尝试了不同时间的这些技巧:-))