代码之家  ›  专栏  ›  技术社区  ›  l33t

如何将表达式<action<t,object>>转换为表达式<action<t,u>>?

  •  1
  • l33t  · 技术社区  · 8 年前

    我有一个接受类型参数的表达式 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 回复  |  直到 8 年前
        1
  •  2
  •   xanatos    8 年前

    简单的解决方案:从C的角度来看,lambda表达式是非类型化的(它的类型是 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();
    

    请注意,如果您需要 LambdaExpression语言 对于某些ORM或其他子系统,并非所有库都支持 Invoke (可以使用表达式重写器删除)或 Convert (这是从值类型转换为引用类型所必需的)。我甚至看过不喜欢的图书馆 object 完全停止(我已经尝试过很多次了:-)