“特殊班级”到底是什么?

在未能编译下面这样的内容之后:

public class Gen<T> where T : System.Array
{
}

犯了错误

约束不能是特殊类‘ System.Array’

我开始想知道,到底是一个什么样的“特殊类”?

当人们在通用约束中指定 System.Enum时,似乎经常会得到同样的错误。我对 System.ObjectSystem.DelegateSystem.MulticastDelegateSystem.ValueType也得到了相同的结果。

还有更多的吗? 我在 C # 中找不到任何关于“特殊类”的信息。

Also, what so special about those classes that we can't use them as a generic type constraint?

7427 次浏览

我不认为存在任何关于“特殊类”/“特殊类型”的官方定义。

你可能认为它们是 a 类型,不能与“常规”类型的语义一起使用:

  • you can't instantiate them directly;
  • you can't inherit custom type from them directly;
  • 有一些编译器的魔力与他们一起工作(可选) ;
  • 直接使用它们的实例至少是无用的(可选地; 想象一下,如果您已经在上面创建了泛型,那么您将编写什么泛型代码?)

另外,我会把 System.Void加到名单上。

根据 MSDN,它是一个静态类列表:

编译器错误 CS0702

约束不能是特殊类“标识符”以下类型不能用作约束:

  • 系统目标
  • System.Array
  • 系统,授权
  • 系统。枚举
  • System.ValueType.

从 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;
}

以下内容可以通过 C # 4th Edition 在 CLR 中找到:

主要限制

类型参数可以指定零个主约束或一个主约束 约束可以是标识未密封类的引用类型。不能指定引用类型 以下特殊参考类型: 系统目标System.Array系统,授权、, 系统, System.ValueType System.Enum系统,无效。 指定引用类型约束时,向编译器承诺指定的类型 参数将属于同一类型或从约束类型派生的类型。

我发现 Jon Skeet 在2008年对一个类似问题的评论: 为什么支持 System.Enum约束 没有

我知道这有点离题,但是他问了 Eric Lippert (C # 团队)这个问题,他们给出了这样的答案:

首先,您的猜想是正确的; 约束的限制 大体上是语言的人工制品,而不是 CLR 我们做这些功能,有一些次要的事情,我们希望 change in the CLR regarding how enumerable types are specified, but 主要是语言方面的工作。)

其次,我个人喜欢有授权约束,enum 约束,以及指定非法约束的能力 today because the compiler is trying to save you from yourself. (That 是将密封类型作为约束合法化,等等。)

However, due to scheduling restrictions, we will likely not be able to get these features into the next version of the language.

As per C# 4.0 Language Specification (Coded : [10.1.5] Type parameter constraints) tells two things:

类型不能是对象。因为所有类型都是从对象派生的, 如果允许的话,这样的约束不会有任何效果。 < br >

如果 T 没有主约束或类型参数约束,则其有效 基类是对象。

定义泛型类时,可以对客户端代码在实例化类时可用于类型参数的类型类型应用限制。如果客户端代码试图使用约束不允许的类型实例化类,则结果为编译时错误。这些限制称为约束。约束是通过使用 where 上下文关键字指定的。 如果要将泛型类型约束为引用类型,请使用: class。

public class Gen<T> where T : class
{
}

这将禁止泛型类型成为值类型,如 int 或 struct 等。

此外,约束不能是特殊类“标识符”以下类型不能用作约束:

  • System.Object
  • 系统数组
  • System.Delegate
  • 系统。枚举
  • System.ValueType.

框架中的某些类有效地将特殊特性传递给从它们派生而来的所有类型 但本身并不具备这些特征。CLR 本身并不禁止使用这些类作为约束,但是受约束的泛型类型不会像具体类型那样获得非继承特征。C # 的创建者决定,因为这样的行为可能会使一些人感到困惑,而且他们看不出这样做有什么用处,所以他们应该禁止这样的约束,而不是允许他们像在 CLR 中那样行事。

例如,如果允许写: void CopyArray<T>(T dest, T source, int start, int count),那么就可以将 destsource传递给需要 System.Array类型参数的方法; 此外,还可以在编译时验证 destsource是兼容的数组类型,但是不能使用 []操作符访问数组的元素。

The inability to use Array as a constraint is mostly pretty easy to work around, since void CopyArray<T>(T[] dest, T[] source, int start, int count) will work in almost all situation where the former method would work. It does, however, have a weakness: the former method would work in the scenario that one or both of the arguments was of type System.Array while rejecting cases where the arguments are incompatible array types; adding an overload where both arguments were of type System.Array would make the code accept the additional cases it should accept, but also make it erroneously accept cases it should not.

我发现取缔大多数特殊约束的决定令人厌烦。唯一一个没有任何语义意义的是 System.Object[因为如果它作为一个约束是合法的,那么任何东西都可以满足它]。System.ValueType可能不太有用,因为 ValueType类型的引用与值类型没有太多共同之处,但在涉及反射的情况下,它可能有一些值。System.EnumSystem.Delegate都有一些实际的用途,但是由于 C # 的创建者没有想到它们,所以它们被取缔了。