Skip to content


Fun with expression trees (.NET v4 RC)

I had some fun with writing unit tests for APM style calls the other day, got interested in the new expression tree-features in .NET v4, and this is basically the fallout.

The task is simple: for a delegate instance of type TDelegate create a new delegate of type TDelegate, which calls the original delegate plus does some other stuff (in my case adding/wrapping the delegate call with exception handling code). The method should be able to work on delegates of any given type (System.AsyncCallback, System.Action<string>, System.Func<string, out int> …), and should treat input, output and return values properly. Note that this is unit testing code so we will not pay a lot of attention on the performance (which will suffer a bit).

In v4, courtesy to the integration of the Dynamic Language Runtime into the framework, expression trees have been pimped quite a bit. For example, you can now create full method bodies.

We want to create a method which takes a delegate instance as parameter and returns a new delegate instance of the same type. Hence we say

image

(Delegates are always classes.)

From the delegate passed to our method, we query the parameters and convert them to ParameterExpression using Expression.Parameter (the second parameter _.Name is somewhat optional and just for debugging purposes). This allows us to access the parameters in the expression tree and pass as parameters when we invoke the original delegate.

image

We want to declare a try/catch/finally block and store the Exception we catch in the catch clause. We declare a ParameterExpression to store the exception

image

Next, we create the expression tree for a try/catch/finally-block (some Expression.Block are redundant … I have other code in there which I removed from the sample).

Please note that the method is declared as Expression.TryCatchFinally(Expression body, Expression finally, params CatchBlock[] catch), so the finally comes before the catch handlers.

image

Code does next-to-nothing:

  • In the try-block, we invoke the original delegate (which has been passed as a parameter to our method), and we reference the array of ParameterExpression we created in step 2.
  • In the finally-block, we invoke an instance method using a MethodCallExpression mcexpDecrement. This method just performs some internal cleanup
  • The catch block references the variable we declared in step 3. It will then call a instance method in the calss using a MethodCallExpression mcexpStore, which takes a single parameter of type Exception, and is passed the variable we declared, xcptnParameterDef. Hence, we pass the Exception instance we just caught (and swallowed) to another method in our class.
  • At the end of the catch-block, using the Expression.Default(…), we return a value from our method. In case our delegate is of type AsyncCallback, its ReturnType would be void. In case our delegate if of type Func<string, out int>, Expression.Default(inputDel.Method.ReturnType) would translate to return default(int) in C#.

image

In the last step, we create a lamdba expression. You can create lambda expressions from delegate types (inputDel.GetType() will return System.AsyncCallback in our sample). As the second parameter we pass the try/catch/finally-block as body of the lambda expression. We give it a random name, and pass the array of ParameterExpression we created in step 2 as the last parameter.

In the second step, we compile the lambda expression to a delegate. Since we created the lambda expression based on our delegate type T (in our sample, AsyncCallback), we can cast it using “as T” syntax back to the delegate type passed to our method.

The expression tree for the lambda method will look similar to this in the debug viewer

.Lambda Fooo<System.AsyncCallback>(System.IAsyncResult $asyncResult) {
    .Try {
        .Block() {
            .Invoke .Constant<System.AsyncCallback>(System.AsyncCallback)($asyncResult) // this will invoke our “logic” for the callback
        }
    } .Catch (System.Exception $xcptn) {
        .Block() {
            .Call .Constant<VisualStudio.TestTools.UnitTesting.Extensions.TestExceptionManager>(VisualStudio.TestTools.UnitTesting.Extensions.TestExceptionManager).StoreException($xcptn)
            ;
            .Default(System.Void)
        }
    } .Finally {
        .Block() {
            .Call .Constant<VisualStudio.TestTools.UnitTesting.Extensions.TestExceptionManager>(VisualStudio.TestTools.UnitTesting.Extensions.TestExceptionManager).DecrementCounter()
        }
    }
}

We can use the code with delegates of any type. Also works with multicast delegates.

image

Posted in .NET 4.0, Productivity, Testing, Utilities.

Tagged with , , , , .


0 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.



Some HTML is OK

or, reply to this post via trackback.