【问题标题】:Expression Tree Copy or Convert表达式树复制或转换
【发布时间】:2011-06-03 20:43:32
【问题描述】:

如何转换一个表达式树的形式

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

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

其中 POCO1 和 POCO2 是 C# 对象,并且都具有 Int32 Age 属性

【问题讨论】:

  • 你想做什么?我的意思是,你为什么要这样复制它?

标签: c# lambda expression-trees dynamic-language-runtime


【解决方案1】:

好吧,您可以制作自定义表达式访问者来替换参数引用并修补成员访问表达式

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);

【讨论】:

    【解决方案2】:

    粗略的步骤:

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

    我猜你在这里得到的类型是一个没有setter的属性。

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

    所以你必须建立一个新的表达式

    这里是方法

    manually build linq expression for x => x.Child == itemToCompare.Child

    【讨论】:

      【解决方案3】:

      理想情况下 - 你没有。创建一个描述 Age 属性的接口,并构建引用 that 的表达式。如果您无法修改 POCO 类型,请使用像 Go 这样的语言,其中接口是隐式的:-)。

      【讨论】:

      • 除了完全控制 POCO1 和 POCO2 的设计之外,还有更多的限制。在一个实际示例中,Expression 将进入像 Entity Framework 之类的 API(我们也无法控制),并且基于该接口构建的 Expression 将与预期的类型不兼容,即使 POCO1 是 IPOCO 而 POCO2 是 IPOCO。 (Expression> 不是 Expression> 甚至 POCO1 是 IPOCO。关于答案的另一部分,推荐使用其他编程语言也无济于事。
      • 是的,那是开玩笑的。但是,通常情况下,您可以使用 Expression&lt;Func&lt;IPOCO, bool&gt;&gt; 而不是等效地消耗 lambda 的 POCO1 或 POCO2 - 如果可以,您可以完全回避这种复杂性。(不幸的是,这个问题没有解释这里的动机)。
      猜你喜欢
      • 2011-04-09
      • 1970-01-01
      • 1970-01-01
      • 2012-01-15
      • 1970-01-01
      • 2013-08-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多