表达式树复制或转换

15

如何将形式为的ExpressionTree转换为

Expression<Func<POCO1, bool>> exp = p => p.Age > 50;

转换为

Expression<Func<POCO2, bool>> exp2 = p => p.Age > 50;

POCO1е’ҢPOCO2йғҪжҳҜC#еҜ№иұЎпјҢ并且дёӨиҖ…йғҪжңүInt32зұ»еһӢзҡ„AgeеұһжҖ§гҖӮ


你想做什么?我的意思是,为什么你想那样复制它? - Tomas Jansson
3个回答

25

你可以创建自定义的表达式访问器,来替换参数引用并修补成员访问表达式。

class Converter<TTo>
{
    class ConversionVisitor : ExpressionVisitor
    {
        private readonly ParameterExpression newParameter;
        private readonly ParameterExpression oldParameter;

        public ConversionVisitor(ParameterExpression newParameter, ParameterExpression oldParameter)
        {
            this.newParameter = newParameter;
            this.oldParameter = oldParameter;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            return newParameter; // replace all old param references with new ones
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.Expression != oldParameter) // if instance is not old parameter - do nothing
                return base.VisitMember(node);

            var newObj = Visit(node.Expression);
            var newMember = newParameter.Type.GetMember(node.Member.Name).First();
            return Expression.MakeMemberAccess(newObj, newMember);
        }
    }

    public static Expression<Func<TTo, TR>> Convert<TFrom, TR>(
        Expression<Func<TFrom, TR>> e
        )
    {
        var oldParameter = e.Parameters[0];
        var newParameter = Expression.Parameter(typeof(TTo), oldParameter.Name);
        var converter = new ConversionVisitor(newParameter, oldParameter);
        var newBody = converter.Visit(e.Body);
        return Expression.Lambda<Func<TTo, TR>>(newBody, newParameter);
    }
}

class A
{
    public int Value { get; set; }
}

class B
{
    public int Value { get; set; }
}

Expression<Func<A, int>> f = x => x.Value;
var f2 = Converter<B>.Convert(f);

1

大致步骤:


获取表达式并将其转换为BinaryExpression
获取左操作数并将其转换为MemberExpression
获取属性所属的基础类型
如果可能,将其更改为新类型。

在此处获得的类型是一个没有setter的属性,正如我所猜测的那样。

Expression<Func<MainWindow, bool >> exp1 = o => this.ActualHeight>50;
var type = ((MemberExpression)((BinaryExpression)exp1.Body).Left).Expression.Type;

因此,您必须构建一个新的表达式

以下是方法:

手动构建linq表达式以获取x => x.Child == itemToCompare.Child


-1
理想情况下,您不需要这样做。创建一个描述年龄属性的接口,并构建表达式以引用该接口。如果无法修改POCO类型,请使用像Go这样的语言,其中接口是隐式的 :-)。

有很多限制,不仅是对POCO1和POCO2设计的完全控制。在实际示例中,表达式将进入像Entity Framework这样的API(我们也无法控制),并且基于接口构建的表达式与预期的类型不兼容,即使POCO1是IPOCO,POCO2也是IPOCO。(Expression<Func<POCO1, bool>>不是Expression<Func<IPOCO, bool>>,即使POCO1是IPOCO。关于答案的另一部分,建议使用其他编程语言也没有帮助。 - g.pickardou
是的,那只是一种幽默的说法。然而,通常情况下,您可以使用 Expression<Func<IPOCO,bool>> 替代一个等效的消耗 lambda 的 POCO1 或 POCO2 - 如果可以的话,就可以完全避开这种复杂性。(不幸的是,问题没有解释这里的动机)。 - Eamon Nerbonne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接