3

I know how to build a simple lambda like 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());

But, how do I create a more complex lambda like this p=>p.FindAttribute("Gender")?.Value == "Female" in the same style as above?

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

Of course I could always create a new method on Person like:

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

But it's still of interest to me if it's possible to construct the complex lambda dynamically!

2 Answers 2

1

This piece of code does the job.

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

Maybe clearer this way

        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")
                )
        );
Sign up to request clarification or add additional context in comments.

Comments

0

Null-conditional operator is a language feature. It can be converted to this code:

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

To build an expression tree you can use 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);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.