为什么在没有 new() 泛型类型约束的情况下允许 Activator.CreateInstance<T>()?

Why is Activator.CreateInstance<T>() allowed without the new() generic type constraint?

在下面显示的示例代码中,"CompileError" 方法将无法编译,因为它需要 CreateWithNew() 方法中所示的 where T : new() 约束。但是,CreateWithActivator<T>() 方法可以在没有约束的情况下编译得很好。

public class GenericTests
{
    public T CompileError<T>() // compile error CS0304
    {
        return new T();
    }

    public T CreateWithNew<T>() where T : new() // builds ok
    {
        return new T();
    }

    public T CreateWithActivator<T>() // builds ok
    {
        return Activator.CreateInstance<T>();
    }
}

这是为什么?

根据, which references MSDN documentation, and this question,泛型中的new T()表达式实际上是实现使用Activator.CreateInstance<T>()。所以我不明白为什么调用 new T() 需要以使用 Activator.CreateInstance<T>().

时可以省略的方式约束泛型类型

或者,反过来问:where T : new() 约束的意义是什么,如果在没有约束的泛型方法中直接创建 T 的实例很容易,直接使用完全相同的底层基础设施?

ActivatorT() 之间存在概念上的差异:

  • Activator.CreateInstance<T> — 我想使用其默认构造函数创建 T 的新实例 — 并抛出一个 Exception 如果没有 (因为发生了一些非常错误的事情,我想自己处理it/throw)

    • 旁注: 请记住 as MSDN says

      In general, there is no use for the CreateInstance<T>() generic method in application code, because the type must be known at compile time. If the type is known at compile time, normal instantiation syntax can be used.

      因为通常在编译时已知 Type 时,您会希望使用构造函数(CreateInstance<T>() uses RuntimeTypeHandle.CreateInstance 速度较慢 [这也是为什么 Activator.CreateInstance<T>本身不需要约束]).

  • T() — 我想调用 T 的空构造函数,据说就像标准构造函数调用一样。

您不希望 "standard" 构造函数调用失败,因为 "No such constructor was found",因此,编译器希望你限制有一个。

不仅如此; 在可能的情况下,您应该更喜欢编译时错误而不是 Exceptions.

事实上 T() 是使用反射在内部实现的,与 "I just want a default instance of T" 的一般情况无关(当然,如果您关心 [=78,内部实现很重要=]...).

这只是糖。如果可以的话,请自制糖。例如,您可以通过反射调用任何类型的几乎任何方法(在某些情况下甚至没有实例!),但这是不对的,您同意吗?你的代码在某些时候会变得不可维护,并且在执行时会弹出很多错误,这是非常糟糕的。所以,如果你能在执行前控制自己——就去做吧。

约束将帮助您在编译时理解它。

Activator.CreateInstance<T>() 方法向用户代码公开,以允许以不同方式使用泛型 class 的可能性,其中一些方式需要类型参数满足某些约束和其中一些没有。例如,class Foo<T> 可能支持以下任何使用模式:

  1. 客户端代码提供了一个函数,该函数 returns 一个新的 T

  2. 客户端代码遵循默认函数,该函数使用其默认构造函数创建新的 T

  3. 客户端代码避免使用 class 的任何功能,这些功能需要它创建 T.

  4. 的新实例

模式#1 和#3 应可用于任何T,而#2 应仅适用于具有无参数构造函数的类型。让 Activator.CreateInstance<T>() 为不受约束的 T 编译,并为恰好具有无参数构造函数的类型 T 工作,使得代码支持所有三种使用模式变得容易。如果 Activator.CreateInstance<T> 有一个 new 约束,将它与没有泛型类型参数一起使用会很尴尬。