when and in what scenario to use Expression Tree
I was reading 开发者_C百科about Expression Tree feature and how you can create delegates using lambda expressions. I still can't get as to in what scenario it is useful and in what real world example should I use it.
The primary use for expression trees is for out-of-process LINQ providers such as LINQ to SQL.
When you write something like this:
var query = people.Where(x => x.Age > 18)
.Select(x => x.Name);
those lambda expressions can either be converted to delegates, which can then be executed (as they are in LINQ to Object) or they can be converted to expression trees, which can be analyzed by the query source and acted on accordingly (e.g. by turning them into SQL, web service calls etc). The difference is that expression trees represent the code as data. They can be compiled into delegates if necessary, but usually (within LINQ anyway) they're never executed directly - just examined to find out the logic they contain.
Expression trees are also used extensively in the Dynamic Language Runtime, where they represent the code which should execute when a dynamic expression is evaluated. Expression trees are well suited for this as they can be composed and broken down again, and after they're compiled the resulting IL is JIT-compiled as normal.
Most developers will never need to mess with the expression tree API, although it has a few other uses.
Aside from LINQ, another very simple use case is to extract both the name and the value of a property. I use this in a fluent API for validating data transfer objects. It's safer to pass one lambda parameter to define both name and value rather than have a second string parameter for the name, and run the risk of developers getting it wrong.
Here's an example (minus all the safety checks and other housekeeping):
public Validator<T> Check<T>(Expression<Func<T>> expr) {
// Analyse the expression as data
string name = ((MemberExpression) expr.Body).Member.Name;
// Compile and execute it to get the value
T value = (expr.Compile())();
return new Validator<T>(name, value);
}
Example of use:
Check(() => x.Name).NotNull.MinLength(1);
Check(() => x.Age).GreaterThan(18);
I used expression trees to make a null-safe evaluator:
string name = myObject.NullSafeEval(x => x.Foo.GetBar(42).Baz.Name, "Default");
This methods analyzes and rewrites the expression tree to insert null checks before each property or method call along the "path" to Name
. If a null is encountered along the way, the default value is returned.
See implementation here
Expression trees are also commonly used to avoid referring to a property by hard-coding its name in a string:
private string _foo;
public string Foo
{
get { return _foo; }
set
{
_foo = value;
OnPropertyChanged(() => Foo);
// Rather than:
// OnPropertyChanged("Foo");
}
}
static string GetPropertyName<T>(Expression<Func<T>> expr)
{
var memberExpr = expr.Body as MemberExpression;
if (memberExpr == null)
throw new ArgumentException("expr", "The expression body must be a member expression");
return memberExpr.Member.Name;
}
protected void OnPropertyChanged<T>(Expression<Func<T>> expr)
{
OnPropertyChanged(GetPropertyName(expr));
}
This enables compile time checking and name refactoring
精彩评论