"Special Class" 到底是什么?

What exactly is a "Special Class"?

编译失败后如下:

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

有错误

A constraint cannot be special class `System.Array'

我开始想,到底是什么一个"special class"?

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

还有更多吗?我在 C# 中找不到有关 "special classes" 的任何信息。

另外, 那些 类 有什么特别之处,以至于我们不能将它们用作通用类型约束?

我不认为 "special classes"/"special types" 有任何官方定义。

你可能会认为它们是一个类型,不能与 "regular" 类型的语义一起使用:

  • 你不能直接实例化它们;
  • 您不能直接从它们继承自定义类型;
  • 有一些编译器魔术可以使用它们(可选);
  • 直接使用它们的实例至少是无用的(可选;想象一下,您已经在上面创建了泛型,您将要编写什么泛型代码?)

P.S。我会将 System.Void 添加到列表中。

根据 MSDN,它是 classes 的静态列表:

编译器错误 CS0702

约束不能是特殊的class 'identifier'以下类型不能用作约束:

  • System.Object
  • System.Array
  • System.Delegate
  • System.Enum
  • 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 中找到:

主要约束

类型参数可以指定零个主约束或一个主约束。初级 约束可以是标识未密封的 class 的引用类型。你不能指定一个 以下特殊引用类型:System.ObjectSystem.ArraySystem.Delegate, System.MulticastDelegate, System.ValueTypeSystem.EnumSystem.Void。 指定引用类型约束时,您向编译器承诺指定类型 参数将是相同类型或派生自约束类型的类型。

我发现了 2008 年 Jon Skeet 对类似问题的评论: 为什么 System.Enum 约束 受支持。

我知道这有点离题,但他问了 Eric Lippert(C# 团队),他们提供了这个答案:

First off, your conjecture is correct; the restrictions on constraints are by and large artefacts of the language, not so much the CLR. (Were we to do these features there would be a few minor things we'd like to change in the CLR regarding how enumerable types are specified, but mostly this would be language work.)

Second, I would personally love to have delegate constraints, enum constraints, and the ability to specify constraints that are illegal today because the compiler is trying to save you from yourself. (That is, making sealed types legal as constraints, and so on.)

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

根据 C# 4.0 语言规范(编码:[10.1.5] 类型参数约束)说明两件事:

1] The type must not be object. Because all types derive from object, such a constraint would have no effect if it were permitted.

2] If T has no primary constraints or type parameter constraints, its effective base class is object.

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

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

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

此外,约束不能是特殊的class 'identifier'以下类型不能用作约束:

  • System.Object
  • System.Array
  • System.Delegate
  • System.Enum
  • System.ValueType.

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

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

无法使用 Array 作为约束的问题基本上很容易解决,因为 void CopyArray<T>(T[] dest, T[] source, int start, int count) 几乎适用于前一种方法适用的所有情况。然而,它确实有一个弱点:前一种方法适用于一个或两个参数都是 System.Array 类型的场景,同时拒绝参数是不兼容的数组类型的情况;添加两个参数均为 System.Array 类型的重载会使代码接受它应该接受的其他情况,但也会使其错误地接受它不应该接受的情况。

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