Friday, 17 June 2011

How to Instantiate a Generic Type With Input Parameters at Runtime?

Background:

There are times when we’d like to remove duplicated codes in instantiating types so we use generics to group similar classes and work with them easier.

Look at these classes:

public abstract class BaseType
{
protected int A { get; set; }
protected int B { get; set; }

protected BaseType(int a, int b)
{
this.A = a;
this.B = b;
}
}

public class Type1 : BaseType
{
public Type1(int a, int b) : base(a, b)
{

}
}

public class Type2 : BaseType
{
public Type2(int a, int b) : base(a, b)
{

}
}

Type1 and Type2 both inherit from the BaseType abstract class both of which require the same name and type of arguments to be instantiated.

Wouldn’t it be nice to have a generic method like

TypeCreator<T>(int a, int b) where T : BaseType

{

// instantiate type with a and b

}

Now I show you different ways to implement this.

Solutions

1) Using Activator.CreateInstance… – anti-pattern

The simplest way to implement this would be to use the Activator.CreateInstance method. There are numerous articles which says this approach has an awful performance and avoid it but I’d just like to show how it simply works:


public static class MyTypeFactory<T> where T : BaseType
{
public static T Create(int a, int b)
{
return (T)Activator.CreateInstance(typeof(T), a, b);
}
}

// and use it like this
MyTypeFactory<Type1>.Create(1, 2);

2) Using a Static Delegate

For this approach to work, you’d need to provide a delegate to all your types in a Factory then use it. So it’s a bit inflexible as it has to know which types it can instantiate but it should have a very good performance:

public static class MyTypeFactory
{
static MyTypeFactory()
{
MethodRunner<Type1>.Func = (a, b) => new Type1(a, b);
MethodRunner<Type2>.Func = (a, b) => new Type2(a, b);
}

public static T Create<T>(int a, int b) where T: BaseType
{
return MethodRunner<T>.Func(a, b);
}

static class MethodRunner<T>
{
public static Func<int, int, T> Func { get; set; }
}
}

The static constructor of the MyTypeFactory class only runs once per application domain the first time the factory is accessed so it should also be thread-safe.

You can simply use it like this:

var type1 = MyTypeFactory.Create<Type1>(1, 2);

3) Using Expression Trees

In this approach, a lambda expression has to be compiled per type. So for each type a new compilation will be done initially then they're stored in a private field for further reuse. This has much better performance in comparison with Activator.CreateInstance.

Another good point about this approach is its flexibility; you don’t have to hard code the constructors like approach 2.

public static class MyTypeFactory<T> where T : BaseType
{
private static readonly Func<int, int, T> MethodDelegate = InitializeMethodDelegate();

private static Func<int, int, T> InitializeMethodDelegate()
{
var constructorInfo = typeof(T).GetConstructor(new[] { typeof(int), typeof(int) });
var param1 = Expression.Parameter(typeof(int));
var param2 = Expression.Parameter(typeof(int));

return Expression.Lambda<Func<int, int, T>>(Expression.New(constructorInfo, param1, param2), param1, param2).Compile();
}

public static T Create(int a, int b)
{
return MethodDelegate(a, b);
}
}

and you can again use it like

var type1 = MyTypeFactory<Type1>.Create(1, 2);

Be careful not to re-comiple the type each time as this may make the performance even worse than Activator.CreateInstance(). Only once per type as shown above.

4) Using Reflection

...
Conclusion:
The factors which should be important in your decision making are:

  • Performance

  • Simplicity of use

  • Flexibility
I think using Expression Trees in the way mentioned above provides the best result considering performance and flexibility.

Read Further:

No comments: