究竟啥是“特级”?
Posted
技术标签:
【中文标题】究竟啥是“特级”?【英文标题】:What exactly is a "Special Class"?究竟什么是“特级”? 【发布时间】:2015-04-30 07:40:43 【问题描述】:在编译失败后:
public class Gen<T> where T : System.Array
有错误
约束不能是特殊类`System.Array'
我开始想,究竟什么是“特殊班级”?
当人们在通用约束中指定System.Enum
时,他们似乎经常遇到相同类型的错误。 System.Object
、System.Delegate
、System.MulticastDelegate
和 System.ValueType
也得到了相同的结果。
还有更多吗?我在 C# 中找不到有关“特殊课程”的任何信息。
另外,这些类有什么特别之处,以至于我们不能将它们用作泛型类型约束?
【问题讨论】:
我不认为这是直接复制。问题不是“为什么我不能将其用作约束”,而是“这些特殊类是什么”。我看过这些问题,它们只是说明了为什么将其用作约束是无用的,而不是解释“特殊类”实际上是什么以及为什么它被认为是特殊的。 根据我的经验,使用但不能直接使用它们,只能通过其他语法隐式使用的类是特殊类。枚举属于同一类别。究竟是什么让它们特别,我不知道。 @AndyKorneyev:这个问题有点不同。我要求定义“特殊类别”和/或这些的综合列表。这个问题只是询问 System.Array 不能成为泛型类型约束的原因。 documentation 声明“[...]只有系统和编译器可以从 Array 类显式派生。”。很可能这就是它成为一个特殊类的原因——编译器会对其进行特殊处理。 @RB.:错误。这个逻辑意味着System.Object
不是一个“特殊类”,因为这是有效的:public class X : System.Object
,但System.Object
仍然是一个“特殊类”。
【参考方案1】:
从 Roslyn 源代码来看,它看起来像是isValidConstraintType
中的硬编码类型列表:
switch (type.SpecialType)
case SpecialType.System_Object:
case SpecialType.System_ValueType:
case SpecialType.System_Enum:
case SpecialType.System_Delegate:
case SpecialType.System_MulticastDelegate:
case SpecialType.System_Array:
// "Constraint cannot be special class '0'"
Error(diagnostics, ErrorCode.ERR_SpecialTypeAsBound, syntax, type);
return false;
isValidConstraintType
in GitHub(更新了新类型)
IsValidConstraintType
is Roslyn Source Browser
我使用GitHub search: "A constraint cannot be special class" 找到了它。
【讨论】:
@kobi 702 变为编译器错误 CS0702,如编译器输出(此问题忽略引用)和其他答案所示。 @AakashM - 谢谢!由于某种原因,我尝试编译并没有得到错误号。然后我花了 几乎 5 分钟才找到答案,而且没有足够的时间来编辑我的评论。悲伤的故事。 @Kobi:你必须查看 output 窗口,在那里你可以找到确切的编译器错误代码号CS0702
。
那么现在真正的问题是为什么这些特殊类?
@DavidGrinberg 也许原因是您不能直接从这些类型继承(object
除外),或者至少与它有关。此外,where T : Array
允许将 Assay 作为 T 传递,这可能不是大多数人想要的。【参考方案2】:
我在 2008 年发现了 Jon Skeet 对类似问题的评论:
为什么System.Enum
约束不受支持。
我知道这有点离题,但他向 Eric Lippert(C# 团队)询问了此事,他们提供了以下答案:
首先,你的猜想是正确的;约束的限制 基本上是语言的产物,而不是 CLR。 (是 我们要做这些功能会有一些我们想做的小事情 CLR 中关于如何指定可枚举类型的更改,但是 主要是语言工作。)
其次,我个人希望有委托约束,枚举 约束,以及指定非法约束的能力 今天是因为编译器正试图将您从自己手中拯救出来。 (那 是,使密封类型作为约束合法,等等。)
但是,由于日程安排的限制,我们可能无法 将这些功能融入到语言的下一个版本中。
【讨论】:
@YuvalItzchakov - 引用 Github\MSDN 更好吗? C# 团队已就该问题或类似问题给出了具体答案。它不会真正伤害任何人。 Jon Skeet 刚刚引用了它们,并且在涉及 C# 时非常可靠.. 没必要生气。我并不是说这不是一个有效的答案:) 只是在 jonskeet 的基础上分享我的想法;p 仅供参考,顺便说一句,我认为您在此处引用的是我。 :-) @EricLippert - 这使得报价更加可靠。 答案中的链接域已失效。【参考方案3】:根据MSDN,这是一个静态的类列表:
编译器错误 CS0702
约束不能是特殊类'标识符' 以下类型不能用作约束:
System.Object System.Array System.Delegate System.Enum System.ValueType.【讨论】:
酷,似乎是正确的答案,很好的发现!但是System.MulticastDelegate
在列表中的哪个位置?
@Mints97:不知道,也许缺少文档?
好像你也不能继承这些类。【参考方案4】:
根据 C# 4.0 语言规范(编码:[10.1.5] 类型参数约束)说明两件事:
1] 类型不能是对象。因为所有类型都派生自对象, 如果允许,这样的约束将无效。
2] 如果 T 没有主约束或类型参数约束,则其有效 基类是对象。
当您定义一个泛型类时,您可以对客户端代码在实例化您的类时可用于类型参数的类型类型施加限制。如果客户端代码尝试使用约束不允许的类型来实例化您的类,则结果是编译时错误。这些限制称为约束。使用 where 上下文关键字指定约束。 如果要将泛型类型限制为引用类型,请使用 : class。
public class Gen<T> where T : class
这将禁止泛型类型成为值类型,例如 int 或 struct 等。
另外,Constraint 不能是特殊类 'identifier' 以下类型不能用作约束:
System.Object System.Array System.Delegate System.Enum System.ValueType.【讨论】:
【参考方案5】:框架中的某些类可以有效地将特殊特征传递给从它们派生的所有类型但它们本身并不具备这些特征。 CLR 本身并没有禁止将这些类用作约束,但受约束的泛型类型不会像具体类型那样获得非继承特性。 C# 的创建者决定,因为这样的行为可能会让一些人感到困惑,并且他们看不到它有任何用处,所以他们应该禁止这样的约束,而不是允许它们像在 CLR 中那样行事。
例如,如果允许写:void CopyArray<T>(T dest, T source, int start, int count)
;可以将dest
和source
传递给需要System.Array
类型参数的方法;此外,可以在编译时验证dest
和source
是兼容的数组类型,但无法使用[]
运算符访问数组的元素。
无法使用Array
作为约束通常很容易解决,因为void CopyArray<T>(T[] dest, T[] source, int start, int count)
几乎适用于前一种方法适用的所有情况。但是,它确实有一个弱点:前一种方法可以在一个或两个参数是System.Array
类型的情况下工作,同时拒绝参数是不兼容的数组类型的情况;添加两个参数均为System.Array
类型的重载将使代码接受它应该接受的其他情况,但也会使其错误地接受它不应该接受的情况。
我发现取缔大多数特殊限制的决定令人讨厌。唯一具有零语义意义的将是System.Object
[因为如果这是合法的约束,任何东西都会满足它]。 System.ValueType
可能不会很有用,因为 ValueType
类型的引用与值类型没有太多共同之处,但在涉及反射的情况下它可能有一些价值。 System.Enum
和 System.Delegate
都会有一些实际用途,但是由于 C# 的创建者没有想到它们,因此它们被无缘无故地取缔了。
【讨论】:
【参考方案6】:可以通过 C# 4th Edition 在 CLR 中找到以下内容:
主要约束
类型参数可以指定零个主要约束或一个主要约束。初级 约束可以是标识非密封类的引用类型。你不能指定一个 以下特殊引用类型:System.Object、System.Array、System.Delegate、 System.MulticastDelegate, System.ValueType、System.Enum 或 System.Void。 指定引用类型约束时,您向编译器承诺指定类型 参数要么是同一类型,要么是从约束类型派生的类型。
【讨论】:
另见:C#LS 10.1.4.1 节:类类型的直接基类不能是以下任何类型:System.Array
、System.Delegate
、System.MulticastDelegate
、System.Enum
或 System.ValueType
。此外,泛型类声明不能使用System.Attribute
作为直接或间接基类。【参考方案7】:
我不认为,“特殊类”/“特殊类型”存在任何官方定义。
您可能会将它们视为 a 类型,它不能与“常规”类型的语义一起使用:
您不能直接实例化它们; 您不能直接从它们继承自定义类型; 可以使用一些编译器魔法(可选); 直接使用它们的实例至少没用(可选;想象一下,您已经在上面创建了泛型,您要编写什么泛型代码?)附:我会将System.Void
添加到列表中。
【讨论】:
System.Void
在用作通用约束时会给出完全不同的错误 =)
@Mints97:是的。但如果问题是关于“特殊”的,那么是的,void
非常特殊。 :)
@Dennis:有几个类型参数限制为System.Array
的代码可以使用Array.Copy
之类的方法将数据从一个移动到另一个;参数类型限制为System.Delegate
的代码将能够在它们上使用Delegate.Combine
并将结果转换为正确的类型。有效利用泛型已知类型 Enum
将为每个此类类型使用一次反射,但泛型 HasAnyFlag
方法可以比非泛型方法快 10 倍。以上是关于究竟啥是“特级”?的主要内容,如果未能解决你的问题,请参考以下文章