为什么允许 Activator.CreateInstance() 没有 new() 泛型类型约束

本文关键字:没有 new 约束 泛型类型 Activator CreateInstance 为什么 | 更新日期: 2023-09-27 18:06:32

在下面显示的示例代码中,"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>();
    }
}

这是为什么呢?

根据引用MSDN文档的 https://stackoverflow.com/a/1649108/531971 和这个问题,泛型中的new T()表达式实际上是使用Activator.CreateInstance<T>()实现的。所以我不明白为什么调用new T()需要以使用 Activator.CreateInstance<T>() 时可以省略的方式约束泛型类型。

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

为什么允许 Activator.CreateInstance<T>() 没有 new() 泛型类型约束

ActivatorT() 之间存在概念上的区别:

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

    • 旁注:请记住,正如 MSDN 所说:

      通常,应用程序代码中不使用CreateInstance<T>()泛型方法,因为必须在编译时知道类型。如果类型在编译时已知,则可以使用正常的实例化语法。

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

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

您不希望"标准"构造函数

调用失败,因为"未找到此类构造函数">,因此,编译器希望您约束存在一个构造函数。

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

事实上,T() 是在内部使用反射实现的,这与"我只想要一个默认的 T 实例"的平均情况无关(当然,如果您关心性能/等,内部实现很重要......

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

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

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

  1. 客户端代码提供返回新T的函数。

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

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

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