【问题标题】:How to change a type in an expression tree?如何更改表达式树中的类型?
【发布时间】:2013-02-18 09:12:34
【问题描述】:

我有这样的方法:

private bool Method_1(Expression<Func<IPerson, bool>> expression)
{
    /* Some code that will call Method_2 */
}

在这种方法中,我想将IPerson 类型更改为另一种类型。我想调用另一个看起来像这样的方法:

private bool Method_2(Expression<Func<PersonData, bool>> expression)
{
    /* Some code */
}

所以,在method_1 中,我需要将IPerson 更改为PersonData。我该怎么做?

编辑:

当我调用时:Method_1(p =&gt; p.Id == 1) 我想“保存”条件 (p.Id == 1) 但我想在另一种类型上执行此条件,即IPerson。所以,我需要更改表达式或使用IPerson 创建一个新表达式

【问题讨论】:

  • IPersonPersonData之间有关系吗?
  • @w0lf 不,他们彼此不认识。 PersonData 是接口的 DTO 版本 (IPerson)。
  • 我不知道该怎么做。您能否发布一个具体表达式的示例以及如何将其转换为使用 PersonData 如果您可以手动执行吗?
  • @w0lf 我已经更新了我的帖子
  • 请不要通过编辑来添加您的问题的答案。如果您的问题已通过答案解决,您可以接受,如果您自己解决了问题,您可以write your own answer 和接受。

标签: c# expression-trees


【解决方案1】:

如果您使用 .net 4 很容易(更新:如注释中所述,ExpressionVisitor 是在版本 4 而不是 4.5 中添加的)它需要对旧框架进行一些谷歌搜索:

有一些假设,但我认为它们对您的 DTO 和实体场景有效 - 访问的属性必须匹配。

class PersonData
{
    public bool Prop { get; set; }
}

interface IPerson 
{
    bool Prop { get; set; }
}

在 .net 4 中定义了 ExpressionVisitor 类,如果您使用较旧的类,这会更容易,然后您需要编写或找到它的实现:

class Visitor<T> : ExpressionVisitor
{
    ParameterExpression _parameter;

    //there must be only one instance of parameter expression for each parameter 
    //there is one so one passed here
    public Visitor(ParameterExpression parameter)
    {
        _parameter = parameter;
    }

    //this method replaces original parameter with given in constructor
    protected override Expression VisitParameter(ParameterExpression node)
    {
        return _parameter;
    }

    //this one is required because PersonData does not implement IPerson and it finds
    //property in PersonData with the same name as the one referenced in expression 
    //and declared on IPerson
    protected override Expression VisitMember(MemberExpression node)
    {
        //only properties are allowed if you use fields then you need to extend
        // this method to handle them
        if (node.Member.MemberType != System.Reflection.MemberTypes.Property)
            throw new NotImplementedException();

        //name of a member referenced in original expression in your 
        //sample Id in mine Prop
        var memberName = node.Member.Name;
        //find property on type T (=PersonData) by name
        var otherMember = typeof(T).GetProperty(memberName);
        //visit left side of this expression p.Id this would be p
        var inner = Visit(node.Expression);
        return Expression.Property(inner, otherMember);
    }
}

概念证明:

class Program
{
   static void Main()
    {
        //sample expression
        Expression<Func<IPerson, bool>> expression = x => x.Prop;

        //parameter that will be used in generated expression
        var param = Expression.Parameter(typeof(PersonData));
        //visiting body of original expression that gives us body of the new expression
        var body = new Visitor<PersonData>(param).Visit(expression.Body);
        //generating lambda expression form body and parameter 
        //notice that this is what you need to invoke the Method_2
        Expression<Func<PersonData, bool>> lambda = Expression.Lambda<Func<PersonData, bool>>(body, param);
        //compilation and execution of generated method just to prove that it works
        var boolValue = lambda.Compile()(new PersonData());
    }
}

请注意,这适用于简单的表达式。如果您需要处理x.Prop.Prop1 &lt; 3,那么您需要进一步扩展它。

【讨论】:

  • 谢谢。问你是否可以在代码中提供一些评论是不是太过分了?我觉得很难理解,因为表达式(树?)不是我真正的专业领域......
  • 非常感谢!差点不敢问,能不能也给主应用提供cmets?
  • 非常感谢 - 再次。我现在正在与 Fields 斗争。显然,lambda 适用于字段。不知道为什么,但我现在正在努力解决这个问题:)。
  • 是的MemberExpression 也将持有字段访问权限,但您需要使用GetField 反射方法而不是GetProperty,当然还有Expression.Field 而不是Expression.Property
  • ExpressionVisitor 已经在 .Net 4.0 中。对于 .Net 3.5,您可以从 IQToolkit 获得它。
【解决方案2】:

这是对@Rafal 解决方案的改进。它允许对复杂对象的属性调用进行管道化。

//sample expression
Expression<Func<IPerson, bool>> expression = x => x.PropA.PropB.PropC == "ABC";

只需将otherMember 更改为使用inner 的类型。

class Visitor<T> : ExpressionVisitor
{
    ParameterExpression _parameter;

    //there must be only one instance of parameter expression for each parameter 
    //there is one so one passed here
    public Visitor(ParameterExpression parameter)
    {
        _parameter = parameter;
    }

    //this method replaces original parameter with given in constructor
    protected override Expression VisitParameter(ParameterExpression node)
    {
        return _parameter;
    }

    //this one is required because PersonData does not implement IPerson and it finds
    //property in PersonData with the same name as the one referenced in expression 
    //and declared on IPerson
    protected override Expression VisitMember(MemberExpression node)
    {
        //only properties are allowed if you use fields then you need to extend
        // this method to handle them
        if (node.Member.MemberType != System.Reflection.MemberTypes.Property)
            throw new NotImplementedException();

        //name of a member referenced in original expression in your 
        //sample Id in mine Prop
        var memberName = node.Member.Name;
        
        /*Fix*/
        //visit left side of this expression p.Id this would be p
        var inner = Visit(node.Expression);
        //find property on type T (=PersonData) by name
        var otherMember = inner.Type.GetProperty(memberName);

        return Expression.Property(inner, otherMember);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-06
    • 2017-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多