为其他对象创建表达式

时间:2019-11-07 20:28:43

标签: c# linq lambda expression

我有给定对象的表达式列表。我需要根据列表中提供的表达式为单独的对象创建匹配的表达式。目前,我正在尝试从表达式中手动提取属性,并为另一个对象重建新的表达式。

foreach(var clause in list)
{
    //this needs to change since not all expressions are binary
    var exp = clause as BinaryExpression;

    var member = exp.Left as MemberExpression;
    var otherObjectMember = Expression.Property(otherObject, member.Member.Name);

    //member does not exist in otherObject
    if (otherObjectMember == null)
        continue;

    //this needs to change to handle other expression types, not only equal
    var otherObjectMemberCheck = Expression.Equal(otherObjectMember, exp.Right);

    //additional processing...
}

上面的代码将在以下示例表达式列表中正常工作:

entity.Enabled == true
entity.Priority == 1

问题是,每个表达式类型的场景都必须手动处理(等于,不等于,包含等)。

以下表达式不起作用:

entity.Name.Contains("CPU")
values.Contains(entity.Name)
entity.Priority < 5
and any other non-binary expressions

我希望有更好的方法来处理这种表达式的重新分配。有人可以指出正确的方向吗? 预先感谢!

3 个答案:

答案 0 :(得分:1)

您可以使用简单的ExpressionVisitor轻松完成此操作。

using System;
using System.Linq.Expressions;

public class Visitor : ExpressionVisitor
{
    public ParameterExpression Target { get; set; } 
    public ParameterExpression Replacement { get; set; } 
    protected override Expression VisitParameter(ParameterExpression node)
    {
        return node == Target ? Replacement : base.VisitParameter(node);
    } 
}

public class Program
{
    public static void Main()
    {
        var obj = Expression.Variable(typeof(string));
        var item = Expression.Property(obj, "Length");

        var otherObj = Expression.Variable(typeof(string));
        var replacedItem = new Visitor()
        {
            Target = obj, 
            Replacement = otherObj, 
        }.Visit(item);
    }
}

访问者将递归访问表达式中的每个节点。找到ParameterExpression时,它将检查是否是我们要替换的变量:如果是,则返回替换值。最终结果是一个与输入相同的表达式,但是每次出现的目标变量都已被替换替换。

答案 1 :(得分:0)

您是否考虑过创建用于构建表达式的适配器对象。然后,您将为该单个Adapter对象拥有一个lambda。适配器应具有目标属性,并将其自身的属性映射到基础目标属性。然后,只需更改Adapter的Target属性即可为不同的对象提供表达式。

答案 2 :(得分:0)

这是一件很不容易的事情,因为它已经设置好了。目前,您基本上必须访问每个表达式的每个成员和值,然后希望以某种方式检测该表达式或将其投影到目标。

这是一个有缺陷的设计。

让您的生活更轻松的是简单地公开所有这些东西共享的界面。

public interface ISearchable
{
    string Name { get; set; }
    bool Enabled { get; set; }
    int Priority { get; set; }
}

然后在可搜索实体上使用它

public class Machine : ISearchable {}
public class Part : ISearchable {}

然后在您的表情结构中

IList<Expression<Func<ISearchable,bool>>> list = new List <Expression<Func<ISearchable,bool>>>();

// apply entire list to ISearchable entities