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.

6 comments:

Jon Skeet said...

Cool... I hadn't seen this.

Of course, the term "expression" is now somewhat dubious...

Marc Gravell said...

Indeed; based on what I can find by search, it looks like it comes from ironruby/php origins. Very interesting.

Barry Kelly said...

Yes, they've pretty much folded the DLR's AST stuff into the Expressions namespace.

This could also be used in the future for a metaprogramming layer in the compiler, to compete with Ruby and its ilk but in a statically typed way.

Leo Bushkin said...

I was unable to get your code example to compile with VS2010 Beta2. I have the project set to target Framework 4.0 - but I get the error:

"System.Linq.Expressions.Expression does not contain a definition for 'Comma'

Am I missing some setting or assembly reference, or is this a redaction to the Expression library in Beta2?

Leo Bushkin said...

I was able to figure it out. It looks like some of the Expression methods have changed - including the way you do assignment. The correct code to generate a property assignment is:

var assign = Expression.Assign( Expression.Property(param, typeof(Foo).GetProperty("Bar"), Expression.Constant("def", typeof(string)));

Marc Gravell said...

Yes, there are a number of changes in the current version. I plan on updating once RTM, since it isn't yet stable.