Activator.CreateInstance <t>与编译表达式。两台不同机器上的反向性能</t>

时间:2012-09-06 20:02:04

标签: c# reflection lambda activator

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

两台计算机都是在.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; }
    }
}

1 个答案:

答案 0 :(得分:8)

至少有两个原因:

  • 第一次调用Type<Test>.New()System.Activator.CreateInstance<Test>()的开销相对较大。因此,我将100000改为10000000。
  • 在发布模式下构建应用程序,在没有调试器的情况下运行它。

通过这两个变化,这两种方法需要大约相同的时间。在我的系统中,对于这两种方法,我在1100到1200之间,有时一个更高,有时另一个是。

请注意Activator.CreateInstance<T>()只能调用默认构造函数,而New()接受许多参数。如果你使New()功能不那么强大,并且总是使用默认构造函数,那么它比我系统上的Activator.CreateInstance<T>()略快。

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