C# > Functional Programming > Lambdas and Expressions > Expression Trees
Building and Compiling Expression Trees for Dynamic Filtering
This snippet demonstrates how to build an expression tree to dynamically filter a collection based on runtime criteria. Expression trees are powerful tools for creating dynamic queries and can be used in scenarios where the filtering logic is not known at compile time.
Concepts Behind the Snippet
Expression trees represent code in a tree-like data structure. Each node in the tree represents an expression, such as a method call or a binary operation. The Expression
class in the System.Linq.Expressions
namespace provides the API for building these trees. The key steps are:
1. **Define Parameters:** Create ParameterExpression
objects to represent the input parameters to the lambda expression. These parameters are what the expression will operate on.
2. **Build the Body:** Construct the body of the expression using methods like Expression.Property
, Expression.Equal
, and Expression.AndAlso
. These methods allow you to create nodes in the expression tree that represent operations on the parameters.
3. **Create the Lambda Expression:** Use Expression.Lambda
to create a lambda expression from the parameter(s) and the body of the expression. This represents the complete expression tree.
4. **Compile the Expression:** Use Compile()
to turn the expression tree into a delegate that can be executed. This allows you to use the dynamically built expression in your code.
Code Example: Dynamic Filtering with Expression Trees
The code defines a Product
class with Name
and Price
properties. The BuildFilter
method dynamically creates a filter based on a property name and value. It builds an expression tree that represents the filter logic and then compiles it into a Func
delegate. This delegate can then be used with the Where
method to filter a list of products. The Main
method demonstrates how to use the BuildFilter
method to filter products by name and price.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
}
public class Example
{
public static Func<Product, bool> BuildFilter(string propertyName, object propertyValue)
{
ParameterExpression parameter = Expression.Parameter(typeof(Product), "p");
// p.PropertyName
MemberExpression property = Expression.Property(parameter, propertyName);
// Convert the value to the property type
ConstantExpression constant = Expression.Constant(propertyValue, property.Type);
// p.PropertyName == propertyValue
BinaryExpression equality = Expression.Equal(property, Expression.Convert(constant, property.Type));
// p => p.PropertyName == propertyValue
Expression<Func<Product, bool>> lambda = Expression.Lambda<Func<Product, bool>>(equality, parameter);
return lambda.Compile();
}
public static void Main(string[] args)
{
List<Product> products = new List<Product>
{
new Product { Name = "Laptop", Price = 1200.00m },
new Product { Name = "Mouse", Price = 25.00m },
new Product { Name = "Keyboard", Price = 75.00m }
};
// Dynamically create a filter to find products with the name "Laptop"
Func<Product, bool> nameFilter = BuildFilter("Name", "Laptop");
List<Product> filteredProductsByName = products.Where(nameFilter).ToList();
Console.WriteLine("Products filtered by name:");
foreach (var product in filteredProductsByName)
{
Console.WriteLine($"Name: {product.Name}, Price: {product.Price}");
}
// Dynamically create a filter to find products with a price of 25.00
Func<Product, bool> priceFilter = BuildFilter("Price", 25.00m);
List<Product> filteredProductsByPrice = products.Where(priceFilter).ToList();
Console.WriteLine("\nProducts filtered by price:");
foreach (var product in filteredProductsByPrice)
{
Console.WriteLine($"Name: {product.Name}, Price: {product.Price}");
}
}
}
Real-Life Use Case
Expression trees are highly beneficial in building dynamic query systems. Consider a scenario where you have a search form with multiple filter criteria. Instead of writing separate queries for each possible combination of filters, you can dynamically construct an expression tree based on the user's input. This leads to more maintainable and efficient code.
Best Practices
ExpressionVisitor
class to traverse and modify the tree in a structured way.
Interview Tip
Be prepared to explain the difference between lambda expressions and expression trees. Lambda expressions are a concise way to represent anonymous functions, while expression trees represent code as data. Expression trees can be compiled into delegates, allowing for dynamic code generation.
When to Use Them
Use expression trees when you need to:
Memory Footprint
Expression trees can have a larger memory footprint compared to compiled code because they store the code's structure as data. However, the benefits of dynamic query generation often outweigh the increased memory usage. Caching compiled expressions helps to mitigate the performance impact.
Alternatives
Pros
Cons
FAQ
-
What is the difference between a lambda expression and an expression tree?
A lambda expression is a short way of writing an anonymous method. An expression tree represents code as a data structure (a tree). An expression tree can be compiled into a delegate (which is like a lambda expression) or it can be analyzed and transformed. -
Why would I use expression trees instead of just writing regular C# code?
Expression trees allow you to dynamically construct code at runtime. This is useful when you need to create queries or filters based on user input or other runtime conditions. You can also serialize an expression tree and send it to another application, such as a database server, to be executed there.