C# 为什么Activator.CreateInstance<;T>;()在没有new()泛型类型约束的情况下是否允许?
在下面显示的示例代码中,“CompileError”方法不会编译,因为它需要C# 为什么Activator.CreateInstance<;T>;()在没有new()泛型类型约束的情况下是否允许?,c#,.net,generics,activator,type-constraints,C#,.net,Generics,Activator,Type Constraints,在下面显示的示例代码中,“CompileError”方法不会编译,因为它需要where t:new()约束,如CreateWithNew()方法中所示。但是,CreateWithActivator()方法在没有约束的情况下编译得很好 public class GenericTests { public T CompileError<T>() // compile error CS0304 { return new T(); } pub
where t:new()
约束,如CreateWithNew()
方法中所示。但是,CreateWithActivator()
方法在没有约束的情况下编译得很好
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>();
}
}
公共类泛型测试
{
public T CompileError()//编译错误CS0304
{
返回新的T();
}
public T CreateWithNew(),其中T:new()//构建确定
{
返回新的T();
}
public T CreateWithActivator()//生成正常
{
返回Activator.CreateInstance();
}
}
为什么会这样
根据引用的和,泛型中的newt()
表达式实际上是使用Activator.CreateInstance()
实现的。所以我不明白为什么调用newt()
需要以一种在使用Activator.CreateInstance()时可以忽略的方式约束泛型类型
或者,用另一种方式来回答这个问题:where T:new()
约束的意义是什么,如果在没有约束的通用方法中很容易创建T
的实例,通过直接使用完全相同的基础结构?在Activator
和T()
之间存在概念上的差异:
Activator.CreateInstance
-我想使用它的默认构造函数创建一个T
的新实例-,如果它没有异常则抛出一个异常(因为发生了一些非常错误的事情,我想自己处理/抛出)
- 旁注:请记住:
通常,应用程序代码中的
CreateInstance()
泛型方法没有任何用处,因为类型必须在编译时已知。如果类型在编译时已知,则可以使用正常的实例化语法
因为通常情况下,当编译时已知类型时(CreateInstance()
速度较慢[这也是Activator.CreateInstance
本身不需要约束的原因]),您会希望使用构造函数
T()
-我想调用T
的空构造函数,应该像标准构造函数调用一样
您不希望“标准”构造函数调用失败,因为“没有找到这样的构造函数”,因此,编译器希望您约束存在一个构造函数
不仅如此在可能的情况下,您应该更喜欢编译时错误而不是异常
T()
是使用反射在内部实现的,这一事实与“我只想要一个T
的默认实例”的一般情况无关(当然,如果您关心性能/等等,内部实现是重要的)。这只是糖分。如果可以的话,自我控制糖。例如,您几乎可以通过反射调用任何类型的任何方法(在某些情况下甚至没有实例!),但这是不对的,您不同意吗?您的代码在某个时候将变得不可维护,并且在执行时会弹出许多错误,这是非常糟糕的。所以,如果你能在行刑前控制自己,那就去做吧
约束将帮助您在编译时理解它。将Activator.CreateInstance()
方法公开给用户代码,以允许泛型类以不同的方式可用,其中一些方式要求类型参数满足某些约束,而另一些则不满足。例如,类Foo
可能支持以下任何一种使用模式:
客户机代码提供了一个返回新T
的函数
客户机代码遵从默认函数,该函数使用默认构造函数创建新的T
客户机代码避免使用类的任何特性来创建T
的新实例
模式#1和#3应可用于任何T
,而#2应仅用于具有无参数构造函数的类型。让Activator.CreateInstance()
为不受约束的T
编译,并为碰巧有无参数构造函数的类型T
工作,使代码能够轻松支持所有三种使用模式。如果Activator.CreateInstance
有一个new
约束,将其与没有约束的泛型类型参数一起使用将非常尴尬。因为CreateInstance()
只使用普通的旧反射,不受任何约束。如果类型有一个默认构造函数,它将创建它。这是一种自我控制。你可以通过反射做几乎所有的事情(甚至破坏所有的OOP原则),但很多人不会欣赏它,因为它很脏。有很多方法可以编写笨拙的代码,让你消除编译时错误,并生成运行时错误。这只是一个例子。例如,dynamic
允许您编写对不存在的函数的调用,因此不会出现编译器错误,而是会出现运行时异常。类似地,这里,CreateInstance
允许您推迟到运行时才发现不存在无参数构造函数。问题“where T:new()约束的要点是什么”可以提炼为“类型约束的要点是什么?”它们都有相同的答案。