Skip to content

Instantly share code, notes, and snippets.

@bymyslf
Created November 30, 2017 11:15
Show Gist options
  • Select an option

  • Save bymyslf/7793841266a94e30fbb70f140d58a991 to your computer and use it in GitHub Desktop.

Select an option

Save bymyslf/7793841266a94e30fbb70f140d58a991 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
public static class TypeActivator
{
/// <summary>
/// Returns an instance of type <typeparamref name="TInstance"/> on which the method is invoked.
/// </summary>
/// <returns>An instance of the given type <typeparamref name="TInstance"/.</returns>
public static TInstance Create<TInstance>()
where TInstance : class
{
return Create<TInstance, TypeToIgnore>(null);
}
/// <summary>
/// Returns an instance of type <typeparamref name="TInstance"/> on which the method is invoked.
/// </summary>
/// <typeparam name="TArg">The type of the argument to pass to the constructor.</typeparam>
/// <param name="argument">The argument to pass to the constructor.</param>
/// <returns>An instance of the given type <typeparamref name="TInstance"/.</returns>
public static TInstance Create<TInstance, TArg>(TArg argument)
where TInstance : class
{
return Create<TInstance, TArg, TypeToIgnore>(argument, null);
}
/// <summary>
/// Returns an instance of type <typeparamref name="TInstance"/> 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="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 type <typeparamref name="TInstance"/.</returns>
public static TInstance Create<TInstance, TArg1, TArg2>(TArg1 argument1, TArg2 argument2)
where TInstance : class
{
return Create<TInstance, TArg1, TArg2, TypeToIgnore>(argument1, argument2, null);
}
/// <summary>
/// Returns an instance of type <typeparamref name="TInstance"/> 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="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 type <typeparamref name="TInstance"/.</returns>
public static TInstance Create<TInstance, TArg1, TArg2, TArg3>(TArg1 argument1, TArg2 argument2, TArg3 argument3)
where TInstance : class
{
return InternalFactory<TArg1, TArg2, TArg3>.Create(typeof(TInstance), argument1, argument2, argument3) as TInstance;
}
/// <summary>
/// Returns an instance of type <typeparamref name="TInstance"/> on which the method is invoked.
/// </summary>
/// <param name="instanceType">The type of object to create.</param>
/// <returns>An instance of the given type <typeparamref name="TInstance"/.</returns>
public static TInstance Create<TInstance>(Type instanceType)
where TInstance : class
{
var newInstanceExpression = Expression.New(instanceType);
var constructorCallingLambda = Expression.Lambda<Func<TInstance>>(newInstanceExpression).Compile();
return constructorCallingLambda() as TInstance;
}
/// <summary>
/// Returns an instance of type <typeparamref name="TInstance"/> on which the method is invoked.
/// </summary>
/// <typeparam name="TArg">The type of the argument to pass to the constructor.</typeparam>
/// <param name="instanceType">The type of object to create.</param>
/// <param name="argument">The argument to pass to the constructor.</param>
/// <returns>A reference to the newly created object.</returns>
public static object Create<TArg>(Type instanceType, TArg argument)
{
return Create<TArg, TypeToIgnore>(instanceType, argument, null);
}
/// <summary>
/// Returns an instance of type <typeparamref name="TInstance"/> 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="instanceType">The type of object to create.</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>A reference to the newly created object.</returns>
public static object Create<TArg1, TArg2>(Type instanceType, TArg1 argument1, TArg2 argument2)
{
return Create<TArg1, TArg2, TypeToIgnore>(instanceType, argument1, argument2, null);
}
/// <summary>
/// Returns an instance of type <typeparamref name="TInstance"/> 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="instanceType">The type of object to create.</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>A reference to the newly created object.</returns>
public static object Create<TArg1, TArg2, TArg3>(Type instanceType, TArg1 argument1, TArg2 argument2, TArg3 argument3)
{
return InternalFactory<TArg1, TArg2, TArg3>.Create(instanceType, argument1, argument2, argument3);
}
// To allow for overloads with differing numbers of arguments, we flag arguments which should be
// ignored by using this Type:
private class TypeToIgnore
{
}
// When you use a static generic type, the C# compiler creates a singleton instance of that type for each unique combination of type parameters you use.
// That means with a static, generic helper class, the compiler would create a type-safe cache
private static class InternalFactory<TArg1, TArg2, TArg3>
{
private static readonly Dictionary<Type, Func<TArg1, TArg2, TArg3, object>> instanceCreationMethodsCache = new Dictionary<Type, Func<TArg1, TArg2, TArg3, object>>();
public static object Create(Type type, TArg1 arg1, TArg2 arg2, TArg3 arg3)
{
CacheInstanceCreationMethodIfRequired(type);
return instanceCreationMethodsCache[type](arg1, arg2, arg3);
}
private static void CacheInstanceCreationMethodIfRequired(Type instanceType)
{
if (instanceCreationMethodsCache.ContainsKey(instanceType))
{
return;
}
var argumentTypes = new[] { typeof(TArg1), typeof(TArg2), typeof(TArg3) };
// Get a collection of the constructor argument Types we've been given; ignore any
// arguments which are of the 'ignore this' Type:
Type[] constructorArgumentTypes = argumentTypes.Where(t => t != typeof(TypeToIgnore)).ToArray();
// Get the Constructor which matches the given argument Types:
var constructor = instanceType.GetConstructor(
BindingFlags.Instance | BindingFlags.Public,
null,
CallingConventions.HasThis,
constructorArgumentTypes,
new ParameterModifier[0]);
var lamdaParameterExpressions = new[]
{
Expression.Parameter(typeof(TArg1), "param1"),
Expression.Parameter(typeof(TArg2), "param2"),
Expression.Parameter(typeof(TArg3), "param3")
};
var constructorParameterExpressions = lamdaParameterExpressions
.Take(constructorArgumentTypes.Length)
.ToArray();
var constructorCallExpression = Expression.New(constructor, constructorParameterExpressions);
var constructorCallingLambda = Expression
.Lambda<Func<TArg1, TArg2, TArg3, object>>(constructorCallExpression, lamdaParameterExpressions)
.Compile();
instanceCreationMethodsCache[instanceType] = constructorCallingLambda;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment