为什么允许 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
和 T()
之间存在概念上的区别:
-
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>
可能支持以下任何使用模式:
-
客户端代码提供返回新
T
的函数。 -
客户端代码遵循默认函数,该函数使用其默认构造函数创建新
T
。 -
客户端代码避免使用类的任何功能,这些功能需要它创建新的
T
实例。
模式 #1 和 #3 应该可用于任何T
,而 #2 应该只适用于具有无参数构造函数的类型。 让Activator.CreateInstance<T>()
为不受约束的T
进行编译,并为恰好具有无参数构造函数的类型T
工作,使得代码支持所有三种使用模式变得容易。 如果Activator.CreateInstance<T>
具有new
约束,则将其与没有泛型类型参数一起使用将非常尴尬。