使用lambda表达式创建一个带反射的对象

时间:2018-03-23 14:54:47

标签: c# reflection lambda collections

我们通过调用Activator.CreateInstance在我们的代码中创建新对象来进行大量反射,但基于this文章,最好使用已编译的lambda表达式来提高性能。 所以我创建了一个静态函数,它创建了一个带有lambda表达式的类的intance:

    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;
        }       
    }

但是在这个实现之后,我尝试通过创建1000个对象实例来分析3个方法(Activator.CreateInstance,Inovke和Lambda Expression)。我对lambda表达式的结果感到非常失望。

然后我在that blog中看到“......重要的是要记住编译只应执行一次,因此应仔细检查代码以避免偶尔重新编译lambdas”

所以我添加了一个缓存,它将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信息作为密钥缓存已编译表达式的好方法吗?

1 个答案:

答案 0 :(得分:2)

你的代码仍然会在之前进行相当多的反思它会检查缓存 - 像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>();
    }
}

这在一般情况下 no 工作 - 如果静态构造函数已经竞争,它只是访问该字段并调用该委托。它甚至避免了字典查找(或者更确切地说:它将它推送到CLR本身实现的类似的东西)。