Piecemeal Expression evaluation

One of the more interesting uses of Expressions is strongly-typed reflection.  It seems that most of the time dealing with expressions, I never care about ever actually evaluating the expression for any reason.  When all I’m doing is parsing and examining the expression, I’ll never really need to evaluate the result.  But any expression can be compiled to a delegate, so you’ll usually see code like this:

[Test]
public void Expression_example()
{
    Expression<Func<Customer, object>> expr = c => c.Name;

    var bob = new Customer {Name = "Bob"};

    Func<Customer, object> func = expr.Compile();

    var result = func(bob);

    result.ShouldEqual("Bob");
}

To create an expression, we need a lambda.  Unfortunately, the only way I’ve seen to create an Expression object is through the compiler.  In the above example, we create an expression that represents a Func<Customer, object>.  That is, the expression must be a lambda that expects a Customer and returns object.  This is the basis of most strongly-typed reflection schemes.  To actually execute the delegate behind the Expression, we call the Compile method, which returns a fully-functioning…function.

If you’re interested in Reflection.Emit, I’d highly suggest popping open the code for System.Linq.Expressions.ExpressionCompiler in Reflector.  That’s the bad boy responsible for turning our Expression into executable code. 

In the above test, the Compile() call returns a strongly-typed delegate; namely, Func<Customer, object>.  We get compile-time safety for calling the lambda, so we can’t call it with something besides a Customer, for example.

But what if we want to compile something else besides the expression as a whole?  And why the heck would we want to in the first place?

Recently, I ran into a situation where I didn’t care about evaluating the entire expression, just one part of it.  I wanted to have all of my HTML ID attributes in an ASP.NET MVC application generated from one place, so that both our front-end and our WatiN tests use the same mechanisms and EditModels to provide an iron-clad compile-safe manner of generating and using IDs (and NAMEs).  The interesting part came when I ran into arrays, where I had an EditModel looking something like this:

public class Customer
{
    public string Name { get; set; }
    public Address[] Addresses { get; set; }
}

public class Address
{
    public string Zip { get; set; }
}

In some form somewhere, a user could edit their list of addresses in one form.  To have all of the form model binding magic work correctly, I needed to take the array index into account, so that this expression:

HtmlIdGenerator.Generate<Customer>(c => c.Addresses[0].Zip);

Would generate a correct HTML NAME attribute, that included the index.  The end result looked something like “Addresses[0].Zip” for the NAME attribute value.

I was able to parse the Expression tree with no worries to get to the actual BinaryExpression that represented the ExpressionType.ArrayIndex expression type.  The problem I ran into was that the array index, in actual use, could be absolutely and anything that resolved to an integer.  This could be a constant value, like the above example, or a closure (where I pass in the array index in a loop), a method call, etc.  A strongly-typed Expression was nowhere to be found, as I had only the Expression type work with.  Its Compile method just returns Delegate, not a fancy Func<> or Action<>, or any other delegate type.

But it turned out not to matter, as Delegate has a DynamicInvoke, which allows me to pass in any parameters and returns any result as object.  With this in mind, I had my piecemeal Expression evaluation:

case ExpressionType.ArrayIndex:
    var binaryExpression = (BinaryExpression) visited;

    Expression indexExpression = binaryExpression.Right;
    Delegate indexAction = Expression.Lambda(indexExpression).Compile();
    int value = (int)indexAction.DynamicInvoke();

With the index value, I could now use that value in my HTML NAME/ID attribute value generator.  It didn’t matter how the array index value was generated, as the Compile() method took care of all the details.  Closures, constants, methods, whatever.

No matter where I am in the Expression tree, I can always Compile() each Expression piece and evaluate it individually.  Any necessary parameters need to be passed in, but for cases like closures, it doesn’t matter.  The closure already keeps track of any method parameters needed.  Neato.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in C#. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://lunaverse.wordpress.com Tim Scott

    We addressed this in MvcContrib…for pretty much the same reason and in pretty much the same way.

    http://code.google.com/p/mvccontrib/source/browse/trunk/src/MvcContrib.FluentHtml/Expressions/ExpressionNameVisitor.cs

    As best I can grasp it, the technique you describe for works for arrays but not for collections. You will notice in our code we handle collections as well. However, our technique still feels a little hackish (testing for a method named “get_Item” with 1 argument). Maybe there is a cleaner way?

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @Tim

    Thanks! I’ll take a look at it…I doubt there’s a much better way, as I believe even the C# “foreach” does something like that, looking for a method called “GetEnumerator” (as well as looking for IEnumerable).

  • http://evain.net/blog/ Jb Evain

    “Unfortunately, the only way I’ve seen to create an Expression object is through the compiler.”

    You can call factory methods on the Expression class to build your own expression tree.

    Here are some examples:

    http://anonsvn.mono-project.com/viewvc/trunk/mcs/class/System.Core/Test/System.Linq.Expressions/

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @Jb

    Thanks for the heads up!