从另外两个创建动态表达式 lambda(链接表达式)
Posted
技术标签:
【中文标题】从另外两个创建动态表达式 lambda(链接表达式)【英文标题】:Create dynamic Expression lambda from two others (chaining the Expressions) 【发布时间】:2011-12-13 23:29:53 【问题描述】:给定一个接受一个标识对象并返回一个属性的 lambda:
Expression<Func<Identification, object>> fx = _ => _.Id;
还有一个将对象转换为标识实例的转换 lambda:
ParameterExpression p = Expression.Parameter(typeof(object), "o");
Expression @new = Expression.Lambda(Expression.Convert(p, typeof(Identification)), p);
如何构建一个新的 lambda,它执行 @new
(取出标识实例)并将结果传递给 fx
。我需要@new
的结果以某种方式绑定到fx
的第一个参数,但我找不到示例。
我需要结果是Expression
,它应该是Expression<Func<object, object>>
类型,它应该将入站参数转换为Identification
,然后获取Id
属性。
【问题讨论】:
【参考方案1】:首先,请注意,如果您适当地键入@new
,这会更容易,即:
LambdaExpression @new = ...
因为这样可以轻松访问@new.Body
和@new.Parameters
;完成了,
Expression.Invoke
在这里很有用:
var combinedParam = Expression.Parameter(typeof(object), "o");
var combined = Expression.Lambda<Func<object, object>>(
Expression.Invoke(fx,
Expression.Invoke(@new, combinedParam)), combinedParam);
虽然为了更简洁的表达式,你也可以使用ExpressionVisitor
来完全替换内部表达式:
var injected = new SwapVisitor(fx.Parameters[0], @new.Body).Visit(fx.Body);
var combined = Expression.Lambda<Func<object, object>>(injected,@new.Parameters);
与:
class SwapVisitor : ExpressionVisitor
private readonly Expression from, to;
public SwapVisitor(Expression from, Expression to)
this.from = from;
this.to = to;
public override Expression Visit(Expression node)
return node == from ? to : base.Visit(node);
这是做什么的:
检查fx.Body
树,将_
(参数)的所有实例替换为@new.Body
(请注意,这将包含对o
参数(又名p
)的引用
然后我们从替换后的表达式构建一个新的 lambda,重新使用来自 @new
的相同参数,以确保我们注入的值将被正确绑定
【讨论】:
嗨,我实际上已经接近尝试了,但在 linqpad 中看起来完全错误。最终的表达式树看起来很可怕,即使简化了——但它完美地完成了工作,谢谢。如果您一次完成所有操作,编译器会生成一个更整洁的树。 (object p) => ((Identification)p).Id 你会得到 Convert(Convert(p).Id) 这真的很整洁。无论如何,谢谢你的回答。 @Jim - 你刷新了吗?第二个版本完全创建o => Convert(Convert(o).Id)
,而不是 o => Invoke(_ => Convert(_.Id), Invoke(o => Convert(o), o))
我已经使用了访问者,并生成了新的表达式。效果很好,谢谢。【参考方案2】:
使用来自Marc Gravell's answer 的代码,您可以通过辅助函数很好地简化此过程:
public static class ExpressionHelper
public static Expression<Func<TFrom, TTo>> Chain<TFrom, TMiddle, TTo>(
this Expression<Func<TFrom, TMiddle>> first,
Expression<Func<TMiddle, TTo>> second
)
return Expression.Lambda<Func<TFrom, TTo>>(
new SwapVisitor(second.Parameters[0], first.Body).Visit(second.Body),
first.Parameters
);
private class SwapVisitor : ExpressionVisitor
private readonly Expression _from;
private readonly Expression _to;
public SwapVisitor(Expression from, Expression to)
_from = from;
_to = to;
public override Expression Visit(Expression node)
return node == _from ? _to : base.Visit(node);
现在看看有多干净!
var valueSelector = new Expression<Func<MyTable, int>>(o => o.Value);
var intSelector = new Expression<Func<int, bool>>(x => x > 5);
var selector = valueSelector.Chain<MyTable, int, bool>(intSelector);
它适用于实体框架和其他需要干净的Expression
而不会尝试在其中调用Func
的东西。
【讨论】:
以上是关于从另外两个创建动态表达式 lambda(链接表达式)的主要内容,如果未能解决你的问题,请参考以下文章
哪位高人指点一下,C#中两个动态lambda 表达式有啥办法合并成一个? 谢谢!