为什么c#编译器在使用new()约束调用new的时候发出Activator.CreateInstance?

当你有如下的代码:

static T GenericConstruct<T>() where T : new() { return new T(); } 

C#编译器坚持发出对Activator.CreateInstance的调用,这比本机构造函数慢得多。

我有以下解决方法:

 public static class ParameterlessConstructor<T> where T : new() { public static T Create() { return _func(); } private static Func<T> CreateFunc() { return Expression.Lambda<Func<T>>( Expression.New( typeof( T ) ) ).Compile(); } private static Func<T> _func = CreateFunc(); } // Example: // Foo foo = ParameterlessConstructor<Foo>.Create(); 

但是为什么这个解决方法应该是必要的,这对我来说是没有意义的。

怀疑这是一个JITting问题。 目前,JIT为所有引用types参数重用相同的生成代码 – 所以List<string>的vtable指向与List<Stream>相同的机器代码。 如果每个new T()调用都必须在JITted代码中解决,那么这将不起作用。

只是一个猜测,但它有一定的意义。

一个有趣的小点:在任何情况下, 不会调用一个值types的无参构造函数,如果有的话(这是罕见的)。 查看我最近的博客文章了解详情。 我不知道是否有任何方法强迫它在expression树。

这可能是因为不清楚T是值types还是引用types。 在非通用情况下创build这两种types会产生非常不同的IL。 面对这种模糊性,C#被迫使用通用的types创build方法。 Activator.CreateInstance符合法案。

快速的实验似乎支持这个想法。 如果您input以下代码并检查IL,则将使用initobj而不是CreateInstance,因为该types没有歧义。

 static void Create<T>() where T : struct { var x = new T(); Console.WriteLine(x.ToString()); } 

切换到类和new()约束,但仍然强制Activator.CreateInstance。

为什么这个解决方法是必要的?

因为new()通用约束被添加到.NET 2.0中的C#2.0中。

与此同时,expression式<T>和朋友被添加到.NET 3.5中。

所以你的解决方法是必要的,因为在.NET 2.0中是不可能的。 同时,(1)使用Activator.CreateInstance()是可能的,(2)IL缺乏实现'new T()'的方法,所以使用Activator.CreateInstance()来实现该行为。

有趣的观察:)

这是您的解决scheme的一个简单的变化:

 static T Create<T>() where T : new() { Expression<Func<T>> e = () => new T(); return e.Compile()(); } 

显然天真(可能慢):)

这是更快一点,因为expression式只编译一次:

 public class Foo<T> where T : new() { static Expression<Func<T>> x = () => new T(); static Func<T> f = x.Compile(); public static T build() { return f(); } } 

分析性能,这种方法就像更详细的编译expression式一样快,而且比new T() (在我的testingPC上快160倍)要快得多。

为了获得更好的性能,构build方法调用可以被消除,函数可以被返回,客户端可以直接caching和调用。

 public static Func<T> BuildFn { get { return f; } }