Introduction
Well, the
sole purpose of writing this article is not to tell you what a factory class or
factory method pattern is.
I am sure there
are better known articles already published in web space.
On the
contrary, I am trying to tell you a new efficient way to implement the factory
class using Expression trees rather than using Reflection.
Background
Try To Read About Factory Method Pattern Implementation using C#.
Here is an article to help you kick start with:-
http://www.codeproject.com/Articles/9319/Creating-a-Class-Factory-with-C-and-NET
Using the code
OK, so let’s
understand how to create an object using Factory method pattern by using
Reflection first.
Here is our
sample factory class diagram:-
Well to make
a simple Reflection based Factory Class for creating our shirt type on the
basis of type name, here is what is required:-
Create a Dictionary with Shirt Type
Name [String] as key and Shirt Type [Type] as value.
User Activator (System. Activator) to
create an instance of Type for the supplied shirt type name.
Below is the
code:-
public Shirt CreateShirt(string shirtType, Color color, string brand)
{
if (!_types.ContainsKey(shirtType))
return null;
var t = _types[shirtType];
return Activator.CreateInstance(t, color, brand) as Shirt;
}
Problem with
System Activator:
Well you might be wondering what is wrong with the above code, let
me tell you how Activator’s CreateInstance works or rather actually creates an
instance to understand why it is slow and need to be replaced.
Here is the definition of CreateInstance Method:-
public static object CreateInstance(Type type, params object[] args);
The first parameter is the type you want the activator to create
an object. Internally, the activator will search this type from its name in
metadata.
Next argument is object array with params keyword, which means you
can supply parameters without making an array.
Internally, activator will try to search for a constructor in the
type whose parameters match in number, order, and type with the arguments supplied
in the form of object array to CreateInstance method.
If args array is an empty array or null, the constructor that
takes no parameters (the default constructor) is invoked.
Now because it is searching through the type using Reflection,
which internally always searches on strings, it turns out to be slow at run-time.
For the above shirt factory, it took around 4.74 seconds on my
Core 2 Duo, 4GB Ram Machine, when I ran the CreateShirt Method for a million
times.
sysActFactory.CreateShirt("Tee", Color.Blue, "Reebok");
Now Let’s see what is
the fix
Expression
Trees Come To Rescue
OK, so how does Expression Trees help in this scenario?
If you try to dissect activator’s CreateInstance method, you will
notice that it’s the metadata search that it has to do every time before
invoking the object, that makes it slow.
So I decided, that if somehow I can replace this search through
metadata my making expressions at compile time, I will be left with only
invoking the expression at runtime.
Well, it was easy, why not supply the right constructor at compile
time and then make an Expression out of it. We can then call this expression at
runtime, which will be much like invoking the constructor directly.
Make sense? Let’s see…
Let’s start by having our custom Activator delegate with the same
signature as CreateInstance.
public delegate object Activator(params object[] args);
We will use this delegate to convert our expression to and return
from our Activator Creation Method.
Here is how the method looks like:-
private static TDelegate DoGetActivator<TDelegate>(ConstructorInfo ctor)
where TDelegate : class
{
var ctorParams = ctor.GetParameters();
var paramExp = Expression.Parameter(typeof (object[]), "args");
var expArr = new Expression[ctorParams.Length];
for (var i = 0; i < ctorParams.Length; i++)
{
var ctorType = ctorParams[i].ParameterType;
var argExp = Expression.ArrayIndex(paramExp, Expression.Constant(i));
var argExpConverted = Expression.Convert(argExp, ctorType);
expArr[i] = argExpConverted;
}
var newExp = Expression.New(ctor, expArr);
var lambda = Expression.Lambda<TDelegate>(newExp, paramExp);
return lambda.Compile();
}
Next you can simple
wrap it up in an extension method and replace the TDelegate Type With
Activator Type.
Here is how it looks like:-
public static Activator GetActivator(this ConstructorInfo ctor)
{
return DoGetActivator<Activator>(ctor);
}
You can have another extension of Type that looks like this:-
public static Activator GetActivator(this Type type)
{
var ci = type.GetConstructors(BindingFlags.Instance |BindingFlags.Public).FirstOrDefault();
return ci.GetActivator();
}
We can also create another
extension to Activator Type to actually call the Constructor, like this:-
public static T CreateInstance<T>(this Activator activator, params object[] args) where T : class
{
return activator(args) as T;
}
Here is how to call
the above set of methods to create an instance of PartyWear Shirt:-
var type = typeof (PartyWear);
var activator = type.GetActivator();
var shirt = activator.CreateInstance<Shirt>(Color.Blue, "Blackberry");
Now, let’s run the litmus test.
We will make two
factories one with System Activator and the other one with Expression Tree
Activator and see the difference:-
Well, well, well, you
can see the difference.
The one with
expression trees took less than half a second to make a million Tee Shirts
where as system Activator took a whopping 4.7 seconds.
So that’s it Folks, Download the code
attached and play with it.
Happy Programming!!!