动态构建Lambda表达式

3

我知道如何构建一个简单的Lambda表达式,例如 x => x > 5:

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个回答

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

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

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