Showing posts with label object factory. Show all posts
Showing posts with label object factory. Show all posts

Thursday, February 24, 2011

A generic object creator using expression trees (part 3)

This blog series contains the following posts:
1. Create an instance of an object using the constructor with no parameters
2. Create an instance of an object using the constructor with parameters
3. Caching the compiled expression tree

In the previous posts in this series I showed how to create an instance of an object, using expression trees, both without parameters, and with parameters. Now, unless the compiled expression tree is saved somewhere it's not going to be very quick, since the compilation of the expression tree into a lambda is the most expensive action.

For this purpose, I'll create a simple class LambdaCacher which will keep a dictionary containing the lambdas with their keys. The key for a lambda is the return type in combination with the types of the constructor arguments. To create a key is simple: just concatenate all Type.FullName values, and I'm using a pipe (|) to separate the full names.

If you look back at the previous post there is a method called CreateLambda in the ObjectCreator class, which creates the lambda. This will be the perfect place to use the new LambdaCacher class.

The LambdaCacher class will be static, and will have two static methods from an external point of view, each with two overloads. A LoadLambda method to retrieve an earlier compiled lambda, and a SaveLambda method to store a compiled lambda.

Internally the LambdaCacher class will have a dictionary of some key, and the lambdas as value. Since the lambdas are specified using generics, the dictionary will have a key of type string, and a value of type object. Some things are added to enforce thread safety.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Playground {
    internal static class LambdaCacher {
        private static readonly object _lock = new object();
        private static readonly Dictionary<string, object> _lambdas = new Dictionary<string, object>();

        private static string GenerateKey(Type type, Type[] types) {
            string key = type.FullName + "|";
            if (types != null) {
                key += string.Join("|", types.Select(t => t.FullName).ToArray());
            }
            return key;
        }

        public static TFunc LoadLambda<TFunc>(Type type) where TFunc : class {
            return LoadLambda<TFunc>(type, null);
        }

        public static TFunc LoadLambda<TFunc>(Type type, Type[] types) where TFunc : class {
            lock (_lock) {
                string key = GenerateKey(type, types);
                if (_lambdas.ContainsKey(key)) {
                    return (TFunc)_lambdas[key];
                }
                return null;
            }
        }

        public static void SaveLambda<TFunc>(TFunc func, Type type) {
            SaveLambda<TFunc>(func, type, null);
        }

        public static void SaveLambda<TFunc>(TFunc func, Type type, Type[] types) {
            lock (_lock) {
                string key = GenerateKey(type, types);
                if (!_lambdas.ContainsKey(key)) {
                    _lambdas.Add(key, func);
                } else {
                    throw new ArgumentException("A lambda is already saved for this type (and parameter types is given).");
                }
            }
        }
    }
}
And now we have to use the new CreateLambda method in the ObjectCreate class.
...
        private static TFunc CreateLambda<TBase, TFunc>(Type type, ParameterExpression[] parameters, Type[] types)
            where TBase : class
            where TFunc : class {
            var constructor = type.GetConstructor(types);
            if (constructor == null)
                throw new Exception(string.Format("Type '{0}' has no constructor with the specified parameters.", type.FullName));

            TFunc lambda = LambdaCacher.LoadLambda<TFunc>(type, types);
            if (lambda != null)
                return lambda;

            Expression expression = Expression.New(constructor, parameters);
            if (type != typeof(TBase)) {
                expression = Expression.Convert(expression, typeof(TBase));
            }
            expression = Expression.Lambda(expression, parameters);
            var creatorExpression = expression as Expression<TFunc>;

            lambda = creatorExpression.Compile();
            LambdaCacher.SaveLambda<TFunc>(lambda, type, types);
            return lambda;
        }
...
Of course, the question is now if it actually makes any difference. A test is in order.

The first test will create 1 object.
Running without the lambda cacher: 0.0103251 seconds
Running with the lambda cacher: 0.0272546 seconds

The first test will create 10 object.
Running without the lambda cacher: 0.0429771 seconds
Running with the lambda cacher: 0.0177748 seconds

The second test will create 100.000 objects.
Running without the lambda cacher: 25.31 seconds
Running with the lambda cacher: 0.0693901 seconds

In the first two tests I noticed that the times were not always identical. I suspect that the times necessary are too small to make a real difference, and some startup costs might be different each time. But the third test does show how effective the caching the of lambdas is.

In the next post I'll do some performance testing comparing Reflection and the usage of expression trees.

The code as it is now can be downloaded here.

Tuesday, December 14, 2010

A generic object creator using expression trees (part 2)

This blog series contains the following posts:
1. Create an instance of an object using the constructor with no parameters
2. Create an instance of an object using the constructor with parameters
3. Caching the compiled expression tree

In the previous post in this series I showed how to create an instance of an object, using expression trees, using the constructor with no parameters. In this post I will show how to do it using a constructor with parameters.

This was a little more difficult. My biggest problem was getting the expression parameters right. Getting the expression itself right was not that difficult, it is almost the same as the previous one.

We now have to use another overload of the new expression. It uses a ConstructorInfo object to specify which constructor overload you want to use, and you have to supply the correct parameters.

First step is to retrieve a ConstructorInfo object. This can be retrieved from the type, using the GetContructor method.
using System;
using System.Reflection;

namespace Playground {
    public static partial class ObjectCreator {
        public static TClass CreateObject<TClass, TParam1>(Func<TParam1> param1Provider) {
            var constructorInfo = typeof(TClass).GetConstructor(new Type[] { typeof(TParam1) });
            // TODO: Finish this
        }
    }
}
Once we have the ConstructorInfo object, we can use that to create a new expression.
using System;
using System.Reflection;
using System.Linq.Expressions;

namespace Playground {
    public static partial class ObjectCreator {
        public static TClass CreateObject<TClass, TParam1>(Func<TParam1> param1Provider) {
            var constructorInfo = typeof(TClass).GetConstructor(new Type[] { typeof(TParam1) });
            var parameter = Expression.Parameter(typeof(TParam1));
            var newExpression = Expression.New(constructorInfo, parameter);
            // TODO: Finish this
        }
    }
}
And now we can create a lambda expression using the new expression.
using System;
using System.Reflection;
using System.Linq.Expressions;

namespace Playground {
    public static partial class ObjectCreator {
        public static TClass CreateObject<TClass, TParam1>(Func<TParam1> param1Provider) {
            var constructorInfo = typeof(TClass).GetConstructor(new Type[] { typeof(TParam1) });
            var parameter = Expression.Parameter(typeof(TParam1));
            var newExpression = Expression.New(constructorInfo, parameter);
            var creatorExpression = Expression.Lambda(newExpression, parameter) as Expression<Func<TParam1, TClass>>;
            // TODO: Finish this
        }
    }
}
Now we only have to compile the expression to get the lambda itself, and execute it.
using System;
using System.Reflection;
using System.Linq.Expressions;

namespace Playground {
    public static partial class ObjectCreator {
        public static TClass CreateObject<TClass, TParam1>(Func<TParam1> param1Provider) {
            var constructorInfo = typeof(TClass).GetConstructor(new Type[] { typeof(TParam1) });
            var parameter = Expression.Parameter(typeof(TParam1));
            var newExpression = Expression.New(constructorInfo, parameter);
            var creatorExpression = Expression.Lambda(newExpression, parameter) as Expression<Func<TParam1, TClass>>;
            var creator = creatorExpression.Compile();
            if (creator != null) {
                return creator(param1Provider());
            }
            throw new Exception("Cannot create instance.");
        }
    }
}
This only gives us a method to use a constructor with a single parameter, but the methods for multiple parameter are essentially copies of the method above. With some refactoring I come to the following code.
using System;
using System.Reflection;
using System.Linq;
using System.Linq.Expressions;

namespace Playground {
    public static partial class ObjectCreator {
        public static TClass CreateObject<TClass, TParam1>(Func<TParam1> param1Provider) {
            var parameters = CreateArray<Type, ParameterExpression>(t => Expression.Parameter(t), typeof(TParam1));
            var types = CreateArray<Type, Type>(t => t, typeof(TParam1));
            var creator = CreateLambda<TClass, Func<TParam1, TClass>>(parameters, types);
            if (creator != null) {
                return creator(param1Provider());
            }
            throw new Exception("Cannot create instance.");
        }

        public static TClass CreateObject<TClass, TParam1, TParam2>(Func<TParam1> param1Provider, Func<TParam2> param2Provider) {
            var parameters = CreateArray<Type, ParameterExpression>(t => Expression.Parameter(t), typeof(TParam1), typeof(TParam2));
            var types = CreateArray<Type, Type>(t => t, typeof(TParam1), typeof(TParam2));
            var creator = CreateLambda<TClass, Func<TParam1, TParam2, TClass>>(parameters, types);
            if (creator != null) {
                return creator(param1Provider(), param2Provider());
            }
            throw new Exception("Cannot create instance.");
        }

        public static TClass CreateObject<TClass, TParam1, TParam2, TParam3>(Func<TParam1> param1Provider, Func<TParam2> param2Provider, Func<TParam3> param3Provider) {
            var parameters = CreateArray<Type, ParameterExpression>(t => Expression.Parameter(t), typeof(TParam1), typeof(TParam2), typeof(TParam3));
            var types = CreateArray<Type, Type>(t => t, typeof(TParam1), typeof(TParam2), typeof(TParam3));
            var creator = CreateLambda<TClass, Func<TParam1, TParam2, TParam3, TClass>>(parameters, types);
            if (creator != null) {
                return creator(param1Provider(), param2Provider(), param3Provider());
            }
            throw new Exception("Cannot create instance.");
        }

        public static TClass CreateObject<TClass, TParam1, TParam2, TParam3, TParam4>(Func<TParam1> param1Provider, Func<TParam2> param2Provider, Func<TParam3> param3Provider, Func<TParam4> param4Provider) {
            var parameters = CreateArray<Type, ParameterExpression>(t => Expression.Parameter(t), typeof(TParam1), typeof(TParam2), typeof(TParam3), typeof(TParam4));
            var types = CreateArray<Type, Type>(t => t, typeof(TParam1), typeof(TParam2), typeof(TParam3), typeof(TParam4));
            var creator = CreateLambda<TClass, Func<TParam1, TParam2, TParam3, TParam4, TClass>>(parameters, types);
            if (creator != null) {
                return creator(param1Provider(), param2Provider(), param3Provider(), param4Provider());
            }
            throw new Exception("Cannot create instance.");
        }

        private static TFunc CreateLambda<TClass, TFunc>(ParameterExpression[] parameters, Type[] types) {
            var constructor = typeof(TClass).GetConstructor(types);
            if (constructor == null)
                throw new Exception(string.Format("Type '{0}' has no constructor with the specified parameters.", typeof(TClass).FullName));

            var newExpression = Expression.New(constructor, parameters);
            var creatorExpression = Expression.Lambda(newExpression, parameters) as Expression<TFunc>;
            return creatorExpression.Compile();
        }

        private static TResult[] CreateArray<TSource, TResult>(Func<TSource, TResult> transformer, params TSource[] source) {
            return source.Select(transformer).ToArray();
        }
    }
}
Now we can create an instance of an object using constructor parameters.
namespace Playground.Program {
    class MyTestClass {
        public int I { get; set; }
        public string J { get; set; }
        public MyTestClass() { }
        public MyTestClass(int i) { I = i; }
        public MyTestClass(int i, string j) : this(i) { J = j; }
    }
    class Program {
        static void Main(string[] args) {
            // Use the generic version with parameters
            MyTestClass o1 = ObjectCreator.CreateObject<MyTestClass, int>(() => 10);
            MyTestClass o2 = ObjectCreator.CreateObject<MyTestClass, int, string>(() => 10, () => "Test");

            List<int> baseList = new List<int>(new int[] { 1, 2, 3, 4, 5 });
            List<int> list = ObjectCreator.CreateObject<List<int>, IEnumerable<int>>(() => baseList);
        }
    }
}
Once again, this is just the generic version, but it is no problem to convert it to parameter versions as I've shown in the previous post in this series. But I'll include that when I upload the complete code, which I'll do in one of the following posts. In the next post I'll be saving the generated lambda's so they can be re-used at a later stage.

Tuesday, December 7, 2010

A generic object creator using expression trees (part 1)

This blog series contains the following posts:
1. Create an instance of an object using the constructor with no parameters
2. Create an instance of an object using the constructor with parameters
3. Caching the compiled expression tree

One of the things I've bumped into in the past years is that using reflection to create an instance of an object is a solution that works, but somehow I do not like it. In the time of .NET 2.0 it was the only option, but with .NET 3.0 another option was made available: expression trees. I like it, but unfortunately haven't used it much. Therefore I went looking for information about expression trees. The msdn site provided me with plenty information.

Expression trees are described on msdn as follows: Expression trees represent code in a tree-like data structure, where each node is an expression, for example, a method call or a binary operation such as x < y.

Today I will show how to create an instance of an object using the constructor with no parameters, using expression trees.

Creating an object without any constructor parameters turned out not to be very difficult.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace Playground {
    public static class ObjectCreator {
        public static TClass CreateObject<TClass>() where TClass : new() {
            Expression<Func<TClass>> creatorExpression =
                Expression.Lambda(
                    Expression.New(typeof(TClass)),
                    null
                ) as Expression<Func<TClass>>;
            Func<TClass> creator = creatorExpression.Compile();
            if (creator != null) {
                return creator();
            }
            throw new Exception("Cannot create instance.");
        }
    }
}
What is going on here? We have a generic method which creates an expression tree, compiles the expression tree into a lambda, and executes the lambda, and returns the result of the executed lambda.

The expression tree is the difficult part of the method. It is a lambda expression since we want it to become a lambda after compilation, and the lambda contains a new expression. The new expression has multiple overloads, and one of them takes a parameter of type Type. This is the overload for no constructor parameters.

Since the new expression only validates the given type at run-time, I've added the new() constraint on the TClass generic parameter, so the check for a parameterless constructor is done at compile-time.

This doesn't work if for some reason, at compile-time, we cannot reference the assembly with the type we need. Say you load an assembly to load a specific type, and you want to create an instance of that type. Than you cannot use the generic version. This means we need another method which can use a parameter of type Type, and create an instance of that.

After some fiddling around I came to the following methods.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace Playground {
    public static partial class ObjectCreator {
        public static TClass CreateObject<TClass>() where TClass : new() {
            return CreateObject<TClass>(typeof(TClass));
        }
        public static object CreateObject(Type type) {
            return CreateObject<object>(type);
        }
        public static TBase CreateObject<TBase>(Type type) {
            Expression expression = Expression.New(type);
            if (type != typeof(TBase)) {
                expression = Expression.Convert(expression, typeof(TBase));
            }
            expression = Expression.Lambda(expression, null);
            Expression<Func<TBase>> creatorExpression = expression as Expression<Func<TBase>>;

            Func<TBase> creator = creatorExpression.Compile();
            if (creator != null) {
                return creator();
            }
            throw new Exception("Cannot create instance.");
        }
    }
}
Now we have three methods.
  • One if you can specify the type at compile time
  • One if you cannot specify the type at compile time
  • One if you cannot specify the type at compile-time, but you can specify a base-class of the type

It was necessary to alter the expression a bit, since the expression created by the lambda expression is of the type that is given, and cannot be cast to type Expression<Func<object>> for example. To work around this, a convert expression is inserted if the type to instance is different from the type to return.

Now we can create instances as follows:
namespace Playground.Program {
    class MyTestClass { }
    class MyDerivedTestClass : MyTestClass { }
    class Program {
        static void Main(string[] args) {
            // Use the generic version
            MyTestClass c = ObjectCreator.CreateObject<MyTestClass>();

            // Use the parameter version
            object o = ObjectCreator.CreateObject(typeof(MyTestClass));

            // Use the baseclass+parameter version
            MyTestClass c2 = ObjectCreator.CreateObject<MyTestClass>(typeof(MyDerivedTestClass));
        }
    }
}
Notes:
  • The first method of the three now calls another overload. Since a new() constraint is added to the generic type TClass, we could simply do 'return new TClass()'.
  • In the next post(s) I will show how to create an instance of an object with constructor parameters. Also I will add caching of the expression trees, since in the above code the expression trees are generated everytime when the method is called, and that is not necessary.