Creating an object with reflection using lambda expressions

We use a lot of thought to call Activator.CreateInstance to create new objects in our code, but based on this article, it is better to use a compiled lambda expression to improve performance. So I created a static function that creates an intance of a class with lambda expressions:

    public static class ClassBuilder
    {

        private delegate T ObjectActivator<T>(params object[] args);

        /// <summary>
        /// This function will create a concrete object of type T
        /// </summary>
        /// <typeparam name="T">Base or concrete of object to return</typeparam>
        /// <param name="type">Concrete type of the object to create</param>
        /// <param name="parameters">paramters to give to the constructor</param>
        /// <returns>Instance of the concrete object</returns>
        public static T CreateInstance<T>(Type type, params object[] parameters)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }
            if (parameters == null)
            {
                throw new ArgumentNullException(nameof(parameters));
            }

            // get the concrete types of given params
            Type[] typedArgs = new Type[parameters.Length];

            for (int i = 0; i < parameters.Length; i++)
            {
                typedArgs[i] = parameters[i].GetType();
            }

            // get the right constructor depending the arguments
            ConstructorInfo ctor = type.GetConstructor(typedArgs);

            if (ctor != null)
            {
                // create the activator
                ObjectActivator<T> createdActivator = GetActivator<T>(ctor);

                // return the concrete object
                return createdActivator(parameters);
            }
            else
            {
                throw new ArgumentException("Unable to find constructor with specified parameters.");
            }
        }


        private static ObjectActivator<T> GetActivator<T>(ConstructorInfo ctor)
        {

            Type type = ctor.DeclaringType;
            ParameterInfo[] paramsInfo = ctor.GetParameters();

            // create parameter of name args as expression (type of args : object[])
            ParameterExpression param = Expression.Parameter(typeof(object[]), "args");

            // create expressions for all the parameters of the constructor with the right type
            Expression[] argsExp = new Expression[paramsInfo.Length];
            for (int i = 0; i < paramsInfo.Length; i++)
            {
                // get the type of the current parameter (parameter a position i)
                Expression idx = Expression.Constant(i);
                Type paramType = paramsInfo[i].ParameterType;

                Expression paramAccessorExp = Expression.ArrayIndex(param, idx);

                // Creates a UnaryExpression that represents a type conversion operation.
                argsExp[i] = Expression.Convert(paramAccessorExp, paramType);
            }

            // Creates a NewExpression that represents calling the specified constructor with the specified arguments.
            NewExpression newExp = Expression.New(ctor, argsExp);

            // Creates a LambdaExpression by first constructing a delegate type.
            LambdaExpression lambdaExpression = Expression.Lambda(typeof(ObjectActivator<T>), newExp, param);

            // Compile function will create a delegate function that represents the lamba expression
            ObjectActivator<T> compiledExpression = (ObjectActivator<T>)lambdaExpression.Compile();

            return compiledExpression;
        }       
    }

But after this implementation, I tried to profile 3 methods (Activator.CreateInstance, Inovke and Lambda Expression), creating 1000 instances of the object. I was really disappointed with the result of the lambda expression.

, "... , , , " .

, , ctor :

    // declaration
    private static ConcurrentDictionary<object, object> _activatorCache = new ConcurrentDictionary<object, object>();


    private static ObjectActivator<T> GetActivator<T>(ConstructorInfo ctor)
    {
        // check if the object is in the cache before creating it
        if (_activatorCache.ContainsKey(ctor))
        {
            return _activatorCache[ctor] as ObjectActivator<T>;
        }


        Type type = ctor.DeclaringType;
        ParameterInfo[] paramsInfo = ctor.GetParameters();

        ...

        // Compile function will create a delegate function that represents the lamba expression
        ObjectActivator<T> compiledExpression = (ObjectActivator<T>)lambdaExpression.Compile();

        // add the compiled expression to the cache
        _activatorCache[ctor] = compiledExpression;
    }

, .

-, ?

ctor ?

+4
1

- , - , GetConstructor, . - :

public static T CreateInstance<T>(...) // ...=whatever args
{
    return CtorCache<T>.Ctor(...); // ...=whatever args
}
private static class CtorCache<T> {
    public static Func<..., T> Ctor; // ...=whatever args
    static CtorCache() {
        // TODO: assign CTOR - either through reflection code,
        // or as a fallback something like:
        // Ctor = args => Activator.CreateInstance<T>();
    }
}

- , . ( : - , CLR).

+2

Source: https://habr.com/ru/post/1695248/


All Articles