Humblecoder

Caution, this blog may be ironically named

Creating Typed Delegates at Runtime Using Expression Trees

| Comments

In my previous post I talked about using the abstract factories pattern instead of the service locator pattern. Towards the end of the post I went on to talk about delegate factories and I want to clear up any misunderstanding before going any further.  I think injecting the factory via the constructor is the right way to do this, the delegate factories I talked about are just a simplification and a way to remove the need to write a lot of simple and repetitive code.  I don’t think it helps that I switched example code towards the end. So here is an example using IFile / FileConsumer.

delegate IFile IFileFactoryForFileName(String fileName);  
public class FileConsumer
{
    private IFileFactoryForFileName CreateFileForFileName;  
    public FileConsumer(IFileFactoryForFileName ifileFactory)
    {
        CreateFileFromFileName = ifileFactory;
    }  
    public void DoSomething()
    {
        IFile fileA = CreateFileForFileName("test.txt");  
        //uses fileA
    }
}

In this example we don’t have an interface and implementation for a factory just the delegate declaration, this would live near the IFile interface, and we will trust the container to wire this up for us when it is resolving the constructor arguments.  This leads nicely into the how.

All About Expression

When first looking at the problem of how to generate the delegates I thought easy, anonymous delegates or lambdas and let type inference take care of the rest but the reality is somewhat more complicated.  The best way to achieve this is using Expression Trees.

Expression trees are the foundation of LINQ and a high level abstraction that allow you to treat a tree of objects as code.  This allows us to build an expression at runtime that represents the required factory then compile it to a lambda and finally the delegate we require.

All this starts at the Expression class, it is an abstract class that is used as a base class for Expression objects that represent the various things we can do.   It, also, has static methods that create the relevant expression objects for us.  The ones we are interested in are:

  • ParameterExpression – This is what it sounds like, it allows us to create a parameter that is going to be passed into and used in by the expression.

  • ConstantExpression – Again it’s obvious what this does, it represents a value that will not change for the life time of the expression.

  • NewArrayExpression – This represents the creation of an array and its content within the expression.

  • MethodCallExpression – This allows the expression to call external methods, so far I’ve only been able to get this to work for public statics but this is not a major problem

  • Finally, LambdaExpression – This allows us to pull all of elements together and compile it into a lambda or, as in this case, a delegate of the type supplied, so long as the return type and parameters match.

Now we know what we are going to be use, lets look at how we generate the factory:

public virtual void RegisterDelegateFactoryForType<TResult, TFactoryDelegateType>()
{
    MethodInfo delegateInvoker = typeof(TFactoryDelegateType).GetMethod("Invoke");
    ParameterExpression[] factoryParams = GetParamsAsExpressions(delegateInvoker);  
    //Build the factory from the template
    MethodInfo mi = typeof(ClassFactory).GetMethod("FactoryTemplate");
    mi = mi.MakeGenericMethod(typeof(TResult));  
    Expression call = Expression.Call(mi, new Expression[] {Expression.Constant(this),
        Expression.NewArrayInit(typeof(Object), factoryParams)} );  
    TFactoryDelegateType factory = Expression.Lambda<TFactoryDelegateType>(call, factoryParams).Compile();  
    _typeFactories.Add(typeof(TFactoryDelegateType), factory as Delegate);
}  
private ParameterExpression[] GetParamsAsExpressions(MethodInfo mi)
{
    List<ParameterExpression> paramsAsExpression = new List<ParameterExpression>();  
    Array.ForEach<ParameterInfo>(mi.GetParameters(),
        p => paramsAsExpression.Add(Expression.Parameter(p.ParameterType, p.Name)));  
    return paramsAsExpression.ToArray();
}  
public static T FactoryTemplate<T>(ClassFactory factory, params Object[] args)
{
    return factory.ManufactureType<T>(args);
}

Starting at the top we use the MethodInfo for the Invoke method of the delegate to get the all the parameters for the required delegate.  We then get a MethodInfo for a template method that uses generics, it is important at this point we replace the generic parameters with the concrete types otherwise the compiling of the lambda expression to the delegate will fail.  It then sets up the call to the template factory method, builds the correctly typed delegate and adds it to a collection of prebuilt delegates ready to be passed into constructors.  This expression tree basically builds a lambda expression that looks like the following:

delegate myInterface myInterfaceFactory(String a, String b)  
//transform to  
(String a, String b) => FactoryTemplate(classfactory, a, b);

Memory and Performance Considerations

Before wrapping up, lets just reflect over the performance and memory implications of this.  We are using delegates and statics, a typical recipe for disaster in the managed world but fear not.  The use of the constant expression that relates to the ClassFactory effectively adds a new root to the ClassFactory instance but the class factory object holds the roots to the delegates.  This means before the ClassFactory instance goes out of scope we need to dispose it correctly to ensure we release the roots to the delegates. In reality, this is unlikely to cause us problems as we would normally want the ClassFactory instance to last as long as the application.

What might be a performance problem though is the potential boxing and unboxing of value types that is caused by passing parameters into the FactoryTemplate method as an Object array.  There is no easy answer to this because when we don’t know up front how many parameters we are going to need or what type.  We could, however, make a micro optimisation to avoid it by offering several overloads that take between 0 and 4 parameters before falling back on to a params function with the idea that most people won’t need that many.  I will leave the exercise of converting the expression tree to use the correct generic template factory to the reader :)

Although I haven’t committed the latest changes, I do have this 90% done and I will push to CodePlex soon.  I am going to pull out he DI stuff I’ve been working on and host it at BitBucket.

Comments