为什么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; } }