Created
November 30, 2017 11:15
-
-
Save bymyslf/7793841266a94e30fbb70f140d58a991 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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