如果您拥有以下代码:
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();
但是,为什么这个解决方案应该是必要的,这对我没有意义。
答案 0 :(得分:9)
我怀疑这是一个JITting问题。目前,JIT为所有引用类型参数重用相同的生成代码 - 因此List<string>
的vtable指向与List<Stream>
相同的机器代码。如果必须在JITted代码中解析每个new T()
调用,那将无效。
只是一个猜测,但它会产生某种的意义。
一个有趣的小观点:在 情况下,值类型的无参数构造函数会被调用,如果有的话(很少见)。有关详细信息,请参阅my recent blog post。我不知道是否有任何方法可以在表达式树中强制它。
答案 1 :(得分:8)
这很可能是因为不清楚T是值类型还是引用类型。在非通用场景中创建这两种类型会产生非常不同的IL。面对这种模糊性,C#被迫使用通用的类型创建方法。 Activator.CreateInstance符合要求。
快速实验似乎支持这一想法。如果键入以下代码并检查IL,它将使用initobj而不是CreateInstance,因为类型没有歧义。
static void Create<T>()
where T : struct
{
var x = new T();
Console.WriteLine(x.ToString());
}
将其切换为类和new()约束但仍会强制激活Activator.CreateInstance。
答案 2 :(得分:3)
为什么这种解决方法是必要的?
因为new()泛型约束被添加到.NET 2.0中的C#2.0。
表达式来; T&GT;与此同时,朋友们加入了.NET 3.5。
因此,您的解决方法是必要的,因为在.NET 2.0中无法实现。同时,(1)使用Activator.CreateInstance()是可能的,(2)IL缺乏实现'new T()'的方法,因此使用Activator.CreateInstance()来实现该行为。
答案 3 :(得分:2)
有趣的观察:)
以下是您的解决方案的更简单的变体:
static T Create<T>() where T : new()
{
Expression<Func<T>> e = () => new T();
return e.Compile()();
}
显然天真(并且可能很慢):)
答案 4 :(得分:2)
这有点快,因为表达式只编译一次:
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();
}
}
分析性能,此方法与更详细的编译表达式一样快,速度比new T()
快得多(在我的测试PC上快160倍)。
为了获得更好的性能,可以消除构建方法调用,并且可以返回函数,客户端可以缓存并直接调用。
public static Func<T> BuildFn { get { return f; } }