18

当您有如下代码时:

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();

但对我来说,为什么需要这种解决方法是没有意义的。

4

5 回答 5

9

怀疑这是一个 JITting 问题。目前,JIT 为所有引用类型参数重用相同的生成代码——因此 aList<string>的 vtable 指向与List<Stream>. new T()如果每个调用都必须在 JITted 代码中解决,那将无法正常工作。

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

一个有趣的小点:在这两种情况下,值类型的无参数构造函数都不会被调用,如果有的话(这非常罕见)。有关详细信息,请参阅我最近的博客文章。我不知道是否有任何方法可以在表达式树中强制它。

于 2008-12-15T07:00:57.873 回答
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。

于 2008-12-15T07:04:51.657 回答
3

为什么需要这种解决方法?

因为 new() 泛型约束已添加到 .NET 2.0 中的 C# 2.0。

同时,Expression<T> 和朋友被添加到 .NET 3.5。

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

于 2009-06-10T19:12:26.360 回答
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 倍)快得多。

为了获得更好的性能,可以消除 build 方法调用,而可以返回 functor,客户端可以缓存并直接调用它。

public static Func<T> BuildFn { get { return f; } }
于 2009-08-15T00:38:22.827 回答
2

有趣的观察:)

这是您的解决方案的一个更简单的变体:

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

显然天真(可能很慢):)

于 2008-12-15T07:17:14.643 回答