8

我和一个朋友正在测试使用编译表达式来创建对象,而不是Activator.CreateInstance<T>遇到了一些有趣的结果。我们发现,当我们在每台机器上运行相同的代码时,我们看到的结果完全相反。他得到了预期的结果,编译表达式的性能明显更好,而我很惊讶地看到Activator.CreateInstance<T>性能提高了 2 倍。

两台计算机都运行在 .NET 4.0 中编译

计算机 1 安装了 .NET 4.5。计算机 2 没有。

计算机 1 超过 100000 个对象:

45ms - Type<Test>.New()
19ms - System.Activator.CreateInstance<Test>();

计算机 2 超过 100000 个对象:

13ms - Type<Test>.New()
86ms - System.Activator.CreateInstance<Test>();

这是代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;

namespace NewNew
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch benchmark = Stopwatch.StartNew();
            for (int i = 0; i < 100000; i++)
            {
                var result = Type<Test>.New();
            }
            benchmark.Stop();
            Console.WriteLine(benchmark.ElapsedMilliseconds + " Type<Test>.New()");

            benchmark = Stopwatch.StartNew();
            for (int i = 0; i < 100000; i++)
            {
                System.Activator.CreateInstance<Test>();
            }
            benchmark.Stop();
            Console.WriteLine(benchmark.ElapsedMilliseconds + " System.Activator.CreateInstance<Test>();");
            Console.Read();
        }


        static T Create<T>(params object[] args)
        {
            var types = args.Select(p => p.GetType()).ToArray();
            var ctor = typeof(T).GetConstructor(types);

            var exnew = Expression.New(ctor);
            var lambda = Expression.Lambda<T>(exnew);
            var compiled = lambda.Compile();
            return compiled;
        }
    }

    public delegate object ObjectActivator(params object[] args);

    public static class TypeExtensions
    {
        public static object New(this Type input, params object[] args)
        {
            if (TypeCache.Cache.ContainsKey(input))
                return TypeCache.Cache[input](args);

            var types = args.Select(p => p.GetType());
            var constructor = input.GetConstructor(types.ToArray());

            var paraminfo = constructor.GetParameters();

            var paramex = Expression.Parameter(typeof(object[]), "args");

            var argex = new Expression[paraminfo.Length];
            for (int i = 0; i < paraminfo.Length; i++)
            {
                var index = Expression.Constant(i);
                var paramType = paraminfo[i].ParameterType;
                var accessor = Expression.ArrayIndex(paramex, index);
                var cast = Expression.Convert(accessor, paramType);
                argex[i] = cast;
            }

            var newex = Expression.New(constructor, argex);
            var lambda = Expression.Lambda(typeof(ObjectActivator), newex, paramex);
            var result = (ObjectActivator)lambda.Compile();
            TypeCache.Cache.Add(input, result);
            return result(args);
        }
    }

    public class TypeCache
    {
        internal static IDictionary<Type, ObjectActivator> Cache;

        static TypeCache()
        {
            Cache = new Dictionary<Type, ObjectActivator>();
        }
    }

    public class Type<T>
    {
        public static T New(params object[] args)
        {
            return (T)typeof(T).New(args);
        }
    }

    public class Test
    {
        public Test()
        {

        }

        public Test(string name)
        {
            Name = name;
        }

        public string Name { get; set; }
    }
}
4

1 回答 1

8

这至少有两个原因:

  • Type<Test>.New()调用或System.Activator.CreateInstance<Test>()第一次的开销比较大。因此,我将 100000 更改为 10000000。
  • 在发布模式下构建您的应用程序,无需调试器即可运行它。

通过这两个更改,这两种方法花费的时间大致相同。在我的系统上,两种方法的结果都在 1100 到 1200 之间,有时一种会高一点,有时另一种会高一些。

注意Activator.CreateInstance<T>()只能调用默认构造函数,而你New()接受多个参数。如果你让你的New()功能不那么强大,并且总是在那里使用默认构造函数,它会比Activator.CreateInstance<T>()我的系统稍微快一点。

另请注意,如果应使用两个相同类型的不同构造函数,则您对带参数的构造函数的处理实际上不起作用,具体取决于传递的参数。您选择一次将哪个构造函数用于整个程序的其余部分。

于 2012-09-06T20:37:35.363 回答