动态构建lambda表达式

时间:2018-09-18 08:01:04

标签: dynamic lambda tree expression expression-trees

我知道如何构建像x => x> 5这样的简单lambda:

int[] nbs = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };            
IEnumerable<int> result1 = nbs.Where(x => x > 5);

ParameterExpression parameter = Expression.Parameter(typeof(int), "x");
ConstantExpression constant = Expression.Constant(5);
BinaryExpression expressionBody = Expression.GreaterThan(parameter, constant);
Expression<Func<int, bool>> expression = Expression.Lambda<Func<int, bool>>(expressionBody, parameter);
IEnumerable<int> result2 = nbs.Where(expression.Compile());

但是,如何以类似上述的样式创建更复杂的lambda,例如 p => p.FindAttribute(“ Gender”)?. Value ==“ Female”

  public class Person
    {
        public bool POI { get; set; } = false;
        public string Name { get; set; }
        public List<Car> Cars { get; set; }
        public List<Attribute> Attributes { get; set; }

        public bool PersonOfInterest()
        {            
            return POI;
        }    

        public Attribute FindAttribute(string name)
        {
            return Attributes.FirstOrDefault(a => a.Name == name);
        }
    }

    public class Attribute
    {
        public string Name { get; set; }
        public string Value { get; set; }

        public Attribute(string name, string value) { Name = name; Value = value; }
    }

    public class Car
    {
        public string Make { get; set; }
        public int Horsepowers { get; set; }
        public string Fuel { get; set; }
    }

    Person p1 = new Person();
    p1.Name = "Thom";
    p1.POI = true;
    p1.Attributes = new List<Attribute>() {new Attribute("Length", "Tall"), new Attribute("Hair", "Long hair")};
    p1.Cars = new List<Car>()
    {
        new Car(){Horsepowers = 100, Make = "Toyota", Fuel = "Diesel"},
        new Car(){Horsepowers = 200, Make = "Fiat", Fuel = "Diesel"},
        new Car(){Horsepowers = 300, Make = "Audi", Fuel = "Diesel"},
        new Car(){Horsepowers = 150, Make = "Ferrari", Fuel = "Petrol"}
    };

    Person p2 = new Person();
    p2.POI = false;
    p2.Attributes = new List<Attribute>() { new Attribute("Nationality", "English"), new Attribute("Gender", "Female") };
    p2.Name = "Sophie";
    p2.Cars = new List<Car>()
    {
        new Car(){Horsepowers = 500, Make = "McLaren", Fuel = "Diesel"},
        new Car(){Horsepowers = 200, Make = "Volvo", Fuel = "Diesel"},
        new Car(){Horsepowers = 300, Make = "Audi", Fuel = "Diesel"},
        new Car(){Horsepowers = 400, Make = "Ferrari", Fuel = "Diesel"}
    };

    IEnumerable<Person> res = persons.Where(p=>p.FindAttribute("Gender")?.Value == "Female");

当然,我总是可以在Person上创建一个新方法,例如:

public bool HasAttributeWithValue(string name, string value)
{
    return FindAttribute(name)?.Value == value;
}

但是,如果可以动态构造复杂的lambda,我仍然很感兴趣!

2 个答案:

答案 0 :(得分:1)

这段代码完成了工作。

    ParameterExpression parameter = Expression.Parameter(typeof(Person), "p");
    ConstantExpression my_null_object = Expression.Constant(null);
    ConstantExpression gender = Expression.Constant("Gender");
    MethodCallExpression methodcall = Expression.Call(parameter, typeof(Person).GetMethod("FindAttribute"), gender);
    BinaryExpression is_null = Expression.Equal(methodcall, my_null_object);
    ConstantExpression constant = Expression.Constant("Female");
    MemberExpression member = Expression.Property(methodcall, typeof(Attribute).GetProperty("Value"));
    BinaryExpression expressionBody = Expression.Equal(member, constant);
    BinaryExpression cond = Expression.AndAlso(Expression.IsFalse(is_null), expressionBody);

也许这样更清晰

        ParameterExpression parameter = Expression.Parameter(typeof(Person), "p");

        ConstantExpression my_null_object = Expression.Constant(null);
        MethodCallExpression methodcall = Expression.Call(parameter, typeof(Person).GetMethod("FindAttribute"), Expression.Constant("Gender"));

        BinaryExpression cond = Expression.AndAlso(

            Expression.IsFalse(Expression.Equal(methodcall, my_null_object)),

            Expression.Equal(
                Expression.Property(methodcall, typeof(Attribute).GetProperty("Value")), 
                Expression.Constant("Female")
                )
        );

答案 1 :(得分:0)

空条件运算符是一种语言功能。可以将其转换为以下代码:

var attribute = p.FindAttribute("Gender");
return attribute == null ? false : attribute.Value == "Female";

要构建表达式树,可以使用Expression.Block

var p = Expression.Parameter(typeof(Person), "p");
var findAttribute = Expression.Call(p,
    typeof(Person).GetMethod(nameof(Person.FindAttribute)),
    Expression.Constant("Gender"));


var attribute = Expression.Parameter(typeof(Attribute), "attribute");
var body = Expression.Block(
    new[] {attribute}, // local variables
    new Expression[]
    {
        Expression.Assign(attribute, findAttribute),
        Expression.Condition(
            Expression.Equal(attribute, Expression.Constant(null)),
            Expression.Constant(false),
            Expression.Equal(
                Expression.PropertyOrField(attribute, "Value"),
                Expression.Constant("Female")))
    }
);

var lambda = Expression.Lambda<Func<Person, bool>>(body, p);