Tuesday 4 November 2008

Future Expressions

Last post, I looked at what Expression offered in C# 3.5; to re-cap, very versatile at building simple queries, but not useful for writing manipulation code - in particular, the only in-built assignment is via Expression.MemberInit etc, which is useful only when creating new objects - or at a push you can perform a single update by obtaining the setter and using Expression.Call.

The interesting news is that this looks very different in .NET 4.0 (based on the public October 2008 CTP). It appears that Expression underpins much of the DLR / dynamic work - so there are all sorts of interesting new options - essentially, Expression is the new CodeDOM... personally I am very excited about this; I've always found Expression a far more intuitive and workable abstraction than the counterparts.

In .NET 3.5, there are 46 values in the ExpressionType enum; in .NET 4.0 (CTP), this jumps to 69, with options for loop constructs (loop/dowhile/break/continue/yield), member assignment, composite statements (block/comma?), and exception handling.

The new entries (again, based on the CTP) are:

  • ActionExpression
  • Assign
  • Block
  • BreakStatement
  • Generator
  • ContinueStatement
  • Delete
  • DoStatement
  • EmptyStatement
  • Extension
  • IndexedProperty
  • LabeledStatement
  • LocalScope
  • LoopStatement
  • OnesComplement
  • ReturnStatement
  • Scope
  • SwitchStatement
  • ThrowStatement
  • TryStatement
  • Unbox
  • Variable
  • YieldStatement

I won't pretend to understand them much yet, but I will be investigating! I also understand that the C# 4.0 compiler doesn't support expressions with statement bodies at the moment (although you can build things by hand - which isn't trivial). It will be interesting to see if this changes (not least because reflector is such a good tool for discovering how you are meant to construct these expressions!).

[update] As an example, I previously gave the example of something you can't do in .NET 3.5:


    Expression<Action<Foo>> action = foo =>
{
foo.Bar = "def";
Console.WriteLine(foo);
};


Well, here it is in .NET 4.0:

   class Foo
{
public string Bar { get; set; }
public override string ToString()
{
return Bar;
}
}
static void Main()
{
var param = Expression.Parameter(typeof(Foo), "foo");
var assign = Expression.AssignProperty(
param,
typeof(Foo).GetProperty("Bar"),
Expression.Constant("def", typeof(string)));
var writeLine = Expression.Call(
typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }),
param);

var action = Expression.Lambda<Action<Foo>>(Expression.Comma(assign, writeLine), param);
var actionDel = action.Compile();
Foo foo = new Foo();
actionDel(foo);
}

And of course, there are obvious issues with existing LINQ implementations: if you use some of the new constructs, you should expect your legacy LINQ provider to barf; and indeed, most of the new options simply don't make sense for a database call, so we shouldn't expect too much to change in LINQ-to-SQL/EF.