Custom Linq Expressions 17 Jan 2011


Between .net 3.5 and .net 4.0, the Linq Expression Tree API changed quite a bit, going from a simple compiler supporting expressions only, to a full fledged compiler supporting expressions as well as statements, on top of which are implemented all DLR based language. For anyone working closely with this API, the Expression Tree spec is a great source of information, the MSDN isn’t very helpful there. When the spec comes to describing the object model for nodes, it differentiates several kind of nodes: The core nodes and the common nodes are those which are implemented inside System.Linq.Expressions, and common nodes are implemented on top of the core nodes, using a process that is called reducing nodes. A reducible node is simply a node that can be compiled as a compound of core nodes. We’re not going to talk about library specific extensions, which are nodes that you would write if you were to implement, say, a LINQ provider. We'll focus on reducible nodes. I took some time last year to implement a few helper nodes that are not available in .net 4.0, but, according to the spec, may be included in a future release. They’re mapped to similar C# constructs, and are pretty useful it you generate complex code using expression trees. Here’s the list of custom expressions I added in Mono.Linq.Expressions, my project to complement the System.Linq.Expression namespace: Even if those map to a C# statement, they’re suffixed with `Expression` for consistency with the whole System.Linq.Expressions namespace. Using them is as simple as, heh, adding a using directive:
using Mono.Linq.Expressions;
And using the factory methods on the CustomExpression class to create those new nodes:
var d = Expression.Parameter (typeof (DisposableType), "d");

var printAndDispose = Expression.Lambda> (
	CustomExpression.Using (
		d,
		Expression.Call (typeof (Console).GetMethod (
			"WriteLine", new [] { typeof (object) }), d)),
	d);
The Expression Tree API makes it easy to write custom reducible expressions, all you have to do, is to inherit from Expression, override CanReduce and NodeType, and implement the Reduce method:
public abstract partial class CustomExpression : Expression {

	public override ExpressionType NodeType {
		get { return ExpressionType.Extension; }
	}

	public override bool CanReduce {
		get { return true; }
	}

	/* ... */
}
If we take, for instance, our UsingExpression, mapping to the C# using statement, Reduce is implemented as follows:
public override Expression Reduce ()
{
	var end_finally = Expression.Label ("end_finally");

	return Expression.Block (
		new [] { variable },
		Expression.Assign (variable, disposable),
		Expression.TryFinally (
			body,
			Expression.Block (
				Expression.Condition (
					Expression.NotEqual (variable, Expression.Constant (null)),
					Expression.Block (
						Expression.Call (
							Expression.Convert (variable, typeof (IDisposable)),
							typeof (IDisposable).GetMethod ("Dispose")),
						Expression.Goto (end_finally)),
					Expression.Goto (end_finally)),
				Expression.Label (end_finally))));
}
Just like a C# using, it will try to execute a block, and whether the block triggered an exception or not, it will call IDisposable.Dispose on the subject of the using statement. Wasn't that easy? Even if those nodes are pretty general, writing custom and specific nodes is a powerful and simple way to factorize code when generating code using the Expression Tree API. In the end, I added in Mono.Linq.Expressions a CustomExpression type, our base class for well, custom expressions. We provide a CustomExpressionVisitor which extends the standard ExpressionVisitor to support our custom expressions, and the CSharpWriter has been updated to support them as well:
[Test]
public void Using ()
{
	var arg = Expression.Parameter (typeof (IDisposable), "arg");

	var lambda = Expression.Lambda> (
		CustomExpression.Using (
			arg,
			Expression.Call (typeof (Console).GetMethod (
				"WriteLine", new [] { typeof (object) }), arg)),
		arg);

	AssertExpression (@"
void (IDisposable arg)
{
	using (arg)
	{
		Console.WriteLine(arg);
	}
}
", lambda);
}
Next feature for Mono.Linq.Expressions: using Mono.Reflection to turn a delegate into an expression of this delegate.