C# 为什么在泛型参数约束中强制执行某些排序?

C# 为什么在泛型参数约束中强制执行某些排序?,c#,generics,constraints,language-design,C#,Generics,Constraints,Language Design,例如,在定义泛型类型参数的约束时,我们必须将class()放在前面,将new()放在后面 为什么这是,为什么我不能把我的约束按任何顺序排列 除了在class/struct之前,在末尾添加new() 例如: protected T Clone<T>() where T : class, ICopyable<T>, new() protectedt Clone(),其中T:class,ICopyable,new() 选择该顺序没有特别的原因。选择的顺序从更一般到更具体,我

例如,在定义泛型类型参数的约束时,我们必须将
class()
放在前面,将
new()
放在后面

为什么这是,为什么我不能把我的约束按任何顺序排列

除了在
class
/
struct
之前,在末尾添加
new()


例如:

protected T Clone<T>() where T : class, ICopyable<T>, new()
protectedt Clone(),其中T:class,ICopyable,new()

选择该顺序没有特别的原因。选择的顺序从更一般到更具体,我认为这是一个相当好的属性


至于“为什么需要命令?”这个问题,对于实现和测试团队来说,更容易由语言强加一个清晰、明确的命令。我们可以允许约束以任何顺序出现,但这给我们带来了什么


我研究语言的时间越长,我就越认为每次你给用户一个选择,你就给了他们一个做出错误选择的机会。C#的一个基本设计原则是,当事情看起来不对劲时,我们会告诉你,并强迫你改正——这不是JavaScript的基本设计原则。它的基本设计原则是“蒙混过关,尽力做到用户的意思”。通过对C#中的正确语法设置更多限制,我们可以更好地确保在程序中良好地表达预期的语义

例如,如果我现在正在设计一种类似C的语言,那么我就不可能有歧义的语法,比如:

class C : X , Y


Y显然是一个接口。是X吗?我们无法从语法上判断X是打算作为接口还是类。只需说,这种模糊性会使检测基类型与接口中的循环等事情变得非常复杂。如果像在VB中那样更加详细,那么对所有相关的人来说都会容易得多

与大多数语法相关的问题一样,基本答案是因为规范这么说。我们从C#5.0规范(第10.1.5节)中获得了以下泛型类型约束语法

类型参数约束:

主要约束条件:

次要限制:

构造函数约束:


Eric Lippert出色地解释了它为何以这种方式设计,因此我不会对此进行阐述。

作为背景,语言规范规定约束必须按主约束、次约束、构造函数约束的顺序排列,其中主约束只是类或结构,次要约束是给定的接口或类类型,构造函数约束只是new()。至于为什么他们被归类为这样的,并要求该顺序,我不知道。也许带注释的语言规范会对此有所帮助?很有趣,谢谢。这就是我特别感兴趣的原因。+1感谢您花时间提供一些关于所做决策类型和原因的见解。我总是觉得奇怪的是,C#表达继承和实现接口的方式不同于Java(Java是显式的),而Java在大多数其他方面都对C#产生了很大的影响。更奇怪的是,正如您所说,这会使编译器的工作变得复杂,“实现和测试团队更容易”。。这让我感到惊讶。尽管较小语法的实现可能更容易一些,但现在您有一个完整的额外功能需要实现和测试:“当顺序错误时生成错误”“每次您给用户一个选择,您都会给他们一个做出错误选择的机会。”,这是真的。
... where T : X, Y
primary-constraint 
secondary-constraints
constructor-constraint 
primary-constraint   ,   secondary-constraints
primary-constraint   ,   constructor-constraint 
secondary-constraints ,   constructor-constraint 
primary-constraint   ,  secondary-constraints   ,   constructor-constraint 
class-type 
class 
struct 
interface-type
type-parameter 
secondary-constraints   ,   interface-type
secondary-constraints   ,   type-parameter
new (   )