【问题标题】:Create an object with reflection using lambda expressions使用 lambda 表达式创建具有反射的对象
【发布时间】:2018-09-02 07:13:38
【问题描述】:

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

    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 表达式)。我对 lambda 表达式的结果感到非常失望。

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

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

【问题讨论】:

  • 你有Type type T。这些代表相同的东西吗?
  • 不一定是@MarcGravell,我们可以给T放一个接口,type代表实现该接口的具体类型。

标签: c# reflection lambda collections


【解决方案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 本身实现的类似东西)。

【讨论】:

  • 这是否代表缓存?如果我理解,根据您的建议,我们将始终在调用 CreateInstance&lt;T&gt;() 时通过反射创建一个实例
  • @cheikhndiaye 这个想法是在静态构造函数中决定做什么,也许通过表达式树创建一个Func&lt;...&gt;,用@ 987654325@ 只是“如果没有更好的可能”;因此“TODO”
猜你喜欢
  • 2017-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
相关资源
最近更新 更多