在下面显示的示例代码中,“CompileError”方法将无法编译,因为它需要where T : new()
约束,如CreateWithNew()
方法所示。但是,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>();
}
}
这是为什么?
根据引用https://stackoverflow.com/a/1649108/531971和MSDN documentation的this question,使用{{1}实际上实现了实现中的new T()
表达式}。所以我不明白为什么调用Activator.CreateInstance<T>()
要求以使用new T()
时可以省略的方式约束泛型类型。
或者,以相反的方式提出问题:Activator.CreateInstance<T>()
约束的重点是什么,如果在没有约束的情况下在通用方法中创建where T : new()
的实例很容易,可以直接使用完全相同的底层基础设施?
答案 0 :(得分:12)
Activator
和T()
之间存在概念差异:
Activator.CreateInstance<T>
- 我想使用默认构造函数 创建T
的新实例,如果没有Exception
则抛出CreateInstance<T>()
一个 (因为发生了一些非常错误的事情,我想自己处理它/扔掉它)。
旁注:请注意as MSDN says:
通常,应用程序代码中的
Type
泛型方法没有用处,因为必须在编译时知道该类型。如果在编译时已知类型,则可以使用正常的实例化语法。
因为通常你会想要在编译时CreateInstance<T>()
已知Activator.CreateInstance<T>
时使用构造函数(T()
uses RuntimeTypeHandle.CreateInstance
这更慢 [这就是为什么T
本身不需要约束] )。
Exceptions
- 我想调用T()
的空构造函数,据说就像标准的构造函数调用一样。
您不希望“标准”构造函数调用失败,因为“没有找到这样的构造函数”,因此,编译器希望您约束有之一。
不止于此; 您应该首选编译时间误差超过T
。
main(void){
int B[4][3] = {{1,0,0}, {1,0,1},{1,1,0},{1,1,1}};
int Y[1][4] = {{1}, {1}, {1}, {0}};
}
内部使用与“我只想要{{1}}”的默认实例的平均情况无关的反射实现{当然,如果你内部实现很重要关心表现/等......)。
答案 1 :(得分:2)
这只是糖。如果可以的话,自控糖。例如,您可以通过反射调用几乎任何类型的任何方法(在某些情况下甚至没有实例!),但这是不对的,你不同意吗?您的代码在某些时候将变得无法维护,并且执行时会弹出许多错误,这非常糟糕。所以,如果你可以在执行前控制自己 - 就这样做吧。
Constraint将帮助您在编译时理解它。
答案 2 :(得分:1)
Activator.CreateInstance<T>()
方法暴露给用户代码,以允许泛型类可能以不同方式使用,其中一些方法需要类型参数满足某些约束,而其中一些不满足。例如,类Foo<T>
可能支持以下任何使用模式:
客户端代码提供的函数返回新的T
。
客户端代码遵循默认函数,该函数使用其默认构造函数创建新的T
。
客户端代码避免使用类的任何要求创建T
的新实例的功能。
模式#1和#3应该可以与任何T
一起使用,而#2应该只适用于具有无参数构造函数的类型。让Activator.CreateInstance<T>()
为无约束的T
编译,并为恰好具有无参数构造函数的类型T
工作,使得代码支持所有三种使用模式变得容易。如果Activator.CreateInstance<T>
有一个new
约束,那么将它与没有一个参数的泛型类型参数一起使用会非常尴尬。