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

use*_*267 17 c# generics performance constructor

如果您有以下代码:

static T GenericConstruct<T>() where T : new()
{
    return new T();
}
Run Code Online (Sandbox Code Playgroud)

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();
Run Code Online (Sandbox Code Playgroud)

但是,为什么这个解决方案应该是必要的,这对我没有意义.

Jon*_*eet 9

怀疑这是一个JITting问题.目前,JIT为所有引用类型参数重用相同的生成代码 - 因此一个List<string>vtable指向与之相同的机器代码List<Stream>.如果new T()必须在JITted代码中解析每个调用,那将无效.

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

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


Jar*_*Par 8

这很可能是因为不清楚T是值类型还是引用类型.在非通用场景中创建这两种类型会产生非常不同的IL.面对这种模糊性,C#被迫使用通用的类型创建方法.Activator.CreateInstance符合要求.

快速实验似乎支持这一想法.如果键入以下代码并检查IL,它将使用initobj而不是CreateInstance,因为类型没有歧义.

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

将其切换为类和new()约束仍会强制激活Activator.CreateInstance.

  • 我想紧接着的后续问题是"为什么没有适当的IL指令用于创建具有适当约束的泛型类型的实例?" 它不像他们从一开始就无法构建它:) (6认同)
  • 只是一个快速的说明,这不再是悲伤的事实.无论约束如何,Roslyn都会输出`Activator.CreateInstance`. (2认同)