Edit: I've now written an improved version of this set of extension methods, which can be found here. Consider these deprecated!
I recently wrote a Type.GetInstance() extension method, and used the opportunity to play around with Expression Trees, which I'd recently read up on in C# in Depth. Here's the set of extension methods I came up with, which allow you to quickly create an instance of a Type from the Type itself; like this:
// No constructor arguments:
MyClass myClassInstance = (MyClass)typeof(MyClass).GetInstance();
// One constructor argument:
MyClass myClassInstance = (MyClass)typeof(MyClass).GetInstance(argument1);
// Three constructor arguments:
MyClass myClassInstance = (MyClass)typeof(MyClass).GetInstance(argument1, argument2, argument3);
Where would you use this? Well, for example to create an instance of an object from a generic type argument; I'm now using it in my generic WCF client. I tend to find myself looking up Types at runtime quite often, and this gives me a neat and fast way of creating instances.
Things to note:
- An Expression Tree is a tree of objects which can be compiled into an executable method.
- The Expression Tree below creates a Func which accepts up to three constructor arguments and returns the constructed object. I wanted overloads which took fewer arguments and for them to call the one which took the most, so I added a private TypeToIgnore class which is used solely to identify a constructor argument which should be ignored when putting together the set of constructor parameters.
- To make extra extension methods which take more arguments, you'd need to add an extra object parameter to the Func.
Here's the C# 4 code; I've commented it quite heavily, so hopefully it'll make sense:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
public static class TypeExtensions
{
// This dictionary will hold a cache of object-creation functions, keyed by the constructor signature
private static readonly Dictionary<string, Func<object, object, object, object>> _instanceCreationMethods =
new Dictionary<string, Func<object, object, object, object>>();
/// <summary>
/// Returns an instance of the <paramref name="type"/> on which the method is invoked.
/// </summary>
/// <param name="type">The type on which the method was invoked.</param>
/// <returns>An instance of the <paramref name="type"/>.</returns>
public static object GetInstance(this Type type)
{
return GetInstance<TypeToIgnore>(type, null);
}
/// <summary>
/// Returns an instance of the <paramref name="type"/> on which the method is invoked.
/// </summary>
/// <typeparam name="TArg">The type of the argument to pass to the constructor.</typeparam>
/// <param name="type">The type on which the method was invoked.</param>
/// <param name="argument">The argument to pass to the constructor.</param>
/// <returns>An instance of the given <paramref name="type"/>.</returns>
public static object GetInstance<TArg>(this Type type, TArg argument)
{
return GetInstance<TArg, TypeToIgnore>(type, argument, null);
}
/// <summary>
/// Returns an instance of the <paramref name="type"/> on which the method is invoked.
/// </summary>
/// <typeparam name="TArg1">The type of the first argument to pass to the constructor.</typeparam>
/// <typeparam name="TArg2">The type of the second argument to pass to the constructor.</typeparam>
/// <param name="type">The type on which the method was invoked.</param>
/// <param name="argument1">The first argument to pass to the constructor.</param>
/// <param name="argument2">The second argument to pass to the constructor.</param>
/// <returns>An instance of the given <paramref name="type"/>.</returns>
public static object GetInstance<TArg1, TArg2>(this Type type, TArg1 argument1, TArg2 argument2)
{
return GetInstance<TArg1, TArg2, TypeToIgnore>(type, argument1, argument2, null);
}
/// <summary>
/// Returns an instance of the <paramref name="type"/> on which the method is invoked.
/// </summary>
/// <typeparam name="TArg1">The type of the first argument to pass to the constructor.</typeparam>
/// <typeparam name="TArg2">The type of the second argument to pass to the constructor.</typeparam>
/// <typeparam name="TArg3">The type of the third argument to pass to the constructor.</typeparam>
/// <param name="type">The type on which the method was invoked.</param>
/// <param name="argument1">The first argument to pass to the constructor.</param>
/// <param name="argument2">The second argument to pass to the constructor.</param>
/// <param name="argument3">The third argument to pass to the constructor.</param>
/// <returns>An instance of the given <paramref name="type"/>.</returns>
public static object GetInstance<TArg1, TArg2, TArg3>(
this Type type,
TArg1 argument1,
TArg2 argument2,
TArg3 argument3)
{
string constructorSignatureKey;
var argumentTypes = new[] { typeof(TArg1), typeof(TArg2), typeof(TArg3) };
CacheInstanceCreationMethodIfRequired(type, argumentTypes, out constructorSignatureKey);
return _instanceCreationMethods[constructorSignatureKey].Invoke(argument1, argument2, argument3);
}
private static void CacheInstanceCreationMethodIfRequired(
Type type,
Type[] argumentTypes,
out string constructorSignatureKey)
{
// Make a constructor signature key unique to the Type and argument we've been given; ignore
// any arguments which are of the 'ignore this' Type:
Type[] constructorArgumentTypes = argumentTypes.Where(t => t != typeof(TypeToIgnore)).ToArray();
constructorSignatureKey = GetConstructorSignatureKey(type, constructorArgumentTypes);
// Bail out if we've already cached the instance creation method:
if (_instanceCreationMethods.ContainsKey(constructorSignatureKey))
{
return;
}
// Get the Constructor which matches the given argument Types:
var constructor = type.GetConstructor(
BindingFlags.Instance | BindingFlags.Public,
null,
CallingConventions.HasThis,
constructorArgumentTypes,
new ParameterModifier[0]);
// Get a set of Expressions representing the parameters which will be passed to the Func:
var lamdaParameterExpressions = GetLambdaParameterExpressions(argumentTypes).ToArray();
// Get a set of Expressions representing the parameters which will be passed to the constructor:
var constructorParameterExpressions = GetConstructorParameterExpressions(
lamdaParameterExpressions,
constructorArgumentTypes).ToArray();
// Get an Expression representing the constructor call, passing in the constructor parameters:
var constructorCallExpression = Expression.New(constructor, constructorParameterExpressions);
// Compile the Expression into a Func which takes three arguments and returns the constructed object:
var constructorCallingLambda = Expression
.Lambda<Func<object, object, object, object>>(constructorCallExpression, lamdaParameterExpressions)
.Compile();
_instanceCreationMethods.Add(constructorSignatureKey, constructorCallingLambda);
}
private static IEnumerable<ParameterExpression> GetLambdaParameterExpressions(Type[] argumentTypes)
{
for (int i = 0; i < argumentTypes.Length; i++)
{
yield return Expression.Parameter(typeof(object), string.Concat("param", i));
}
}
private static IEnumerable<UnaryExpression> GetConstructorParameterExpressions(
ParameterExpression[] lamdaParameterExpressions,
Type[] constructorArgumentTypes)
{
for (int i = 0; i < constructorArgumentTypes.Length; i++)
{
// Each parameter passed to the lambda is of type object, so we need to convert it into
// the appropriate type for the constructor:
yield return Expression.Convert(lamdaParameterExpressions[i], constructorArgumentTypes[i]);
}
}
private static string GetConstructorSignatureKey(Type type, Type[] argumentTypes)
{
return string.Concat(type.FullName, " (", string.Join(", ", argumentTypes.Select(at => at.FullName)), ")");
}
// To allow for overloads with differing numbers of arguments, we flag arguments which should be
// ignored by using this Type:
private class TypeToIgnore
{
}
}