As I mentioned in my last blog post, .NET 4.0 has introduced an extension expression model. Now, what did I mean by that, and why is it important?
Many LINQ providers define their own custom expression types that derive from the Expression base class and can be inserted into the expression tree. A SqlColumnExpression, for example, might represent a SQL column in an expression tree that is meant to be converted to SQL. A VBCompareExpression might represent a comparison with VB-specific semantics. In previous versions, the .NET framework provided little support for that; among other things, you had to use undefined ExpressionType enumeration values to represent your expressions, and including visitor support for them was a difficult thing.
With .NET 4.0, it’s now possible to define extension expressions with the following features:
- They need not use an undefined ExpressionType value, they can use the value ExpressionType.Extension.
- They can implement an Accept method to delegate to a specific visitor method if the visitor supports the custom expression type. If it does not, they can dispatch to a generic VisitExtension method (see below).
- Extension expressions can also be reduced to a semantically equivalent tree of standard expression nodes, if possible. This enables high-level nodes to be compiled to IL, for example, which wouldn’t make much sense for a SqlColumnExpression, but would be handy for a ForEachExpression or a VBCompareExpression, for example.
As noted above, ExpressionVisitor now sports a VisitExtension method, which is by default invoked by the Accept methods of extension expressions. This can be used by visitors to react on extension expression types unbeknown to them.
The interesting thing about VisitExtension is its default implementation, which is to call Expression.VisitChildren. VisitChildren enables extension expressions to apply visitors to their child nodes even when those visitors do not know how to handle the specific extension expression types. In a way, this overcomes the big problem of the Visitor design pattern, where the visitors need to (statically) know about the whole type hierarchy of the objects being visited.
To understand the implications of this, consider a generic filtering visitor that replaces all ConstantExpression instances with SqlParameterExpressions. The VisitChildren approach allows the visitor to replace ConstantExpressions that are located beneath a VBCompareExpression even when it doesn’t know anything about that specific expression kind.
Here’s a picture of the visitor in action:
Without the VisitChildren approach, the visitor – because it knows nothing of VBCompareExpressions – would have no way of inspecting those ConstantExpressions.
As I said in my last post, I think .NET 4.0 expression trees do add a lot to their 3.5 counterparts, and the extension expression model is one of the most interesting new features. Because I like that concept so much, I’m going to copy it for re-linq. That way, every LINQ provider based on re-linq will be able to use extension expressions for both .NET 3.5 and .NET 4.0. And, if you haven’t guessed by now, I’ll use that model for finally implementing VB.NET support in re-linq. But more on that in a future blog post.