什么是“特殊班级”?


114

未能获得如下所示的内容后:

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

与错误

约束不能是特殊类`System.Array'

我开始怀疑,到底什么 “特殊班”?

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

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

此外,什么如此特殊的课程,我们不能把它们作为一个泛型类型约束?


14
我不认为这是直接重复。问题不是“我为什么不能使用它作为约束”,而是“这些特殊类是什么”。我看过这些问题,它们只是陈述了为什么将其用作约束是没有用的,而不是解释“特殊类”实际上是什么以及为什么认为它是特殊的。
亚当·霍兹沃思

2
以我的经验,使用过但不能直接使用(仅通过其他语法隐式)的类是特殊类。枚举属于同一类别。我不知道是什么使它们特别与众不同。
Lasse V. Karlsen

@AndyKorneyev:这个问题有点不同。我要求定义“特殊类别”和/或这些类别的综合清单。该问题只是出于系统System.Array不能成为通用类型约束的原因。
Mints97 2015年

文档中可以看出,“只有系统和编译器才能从Array类显式派生”。这很可能使它成为一个特殊的类-编译器对其进行了特殊处理。
RB。

1
@RB .:错。这个逻辑将意味着System.Object不是一个“特别班”,因为这是有效的:public class X : System.Object { },但System.Object仍然是一个“特殊类”。
Mints97 2015年

Answers:


106

从Roslyn源代码看,它看起来像一个硬编码类型的列表:

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

来源:Binder_Constraints.cs IsValidConstraintType
我已经使用GitHub搜索找到它:“约束不能是特殊类”


1
@kobi 702变成编译器错误CS0702,如编译器输出(此问题被忽略引用)和其他答案所示。
AakashM 2015年

1
@AakashM-谢谢!由于某种原因,我尝试编译并且没有获得错误号。然后,我花了将近 5分钟的时间才找到答案,而且没有足够的时间来编辑我的评论。悲剧。
Kobi 2015年

1
@Kobi:您必须查看输出窗口,在其中找到确切的编译器错误代码编号CS0702
蒂姆·施梅尔特

9
因此,现在真正的问题是,为什么这些特殊的课程呢?
大卫说,请

@DavidGrinberg也许原因是您不能直接从这些类型继承(除外object),或者至少与它有关系。还where T : Array可以通过As作为T,这可能不是大多数人想要的。
IllidanS4希望莫妮卡回到

42

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

我知道这不是一个话题,但是他向Eric Lippert(C#团队)询问了有关问题,他们提供了以下答案:

首先,您的猜想是正确的;约束的限制主要是语言的人工制品,而不是CLR。(如果要执行这些功能,则我们希望在CLR中更改一些关于如何指定可枚举类型的小事情,但这主要是语言工作。)

其次,我个人希望拥有委托约束,枚举约束以及能够指定当今不合法的约束的能力,因为编译器正试图将您从自己身上救出来。(也就是说,将密封类型合法化为约束,依此类推。)

但是,由于日程安排的限制,我们可能无法将这些功能引入该语言的下一版本。


10
@YuvalItzchakov-引用Github \ MSDN更好吗?C#团队已针对该问题或类似问题给出了具体的答案。乔恩斯基特刚才引述他们,就是当它到达C#..相当可靠
阿米尔·波波维奇

5
无需生气。我并不是说这不是一个正确的答案:)只是在jonskeet的基础上分享我的想法; p
Yuval Itzchakov

40
顺便说一句,我想你是在那里引用我的。:-)
埃里克·利珀特

2
@EricLippert-这使报价更加可靠。
阿米尔·波波维奇

答案中的链接域已死。

25

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

编译器错误CS0702

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

  • 系统对象
  • 系统数组
  • 系统代理
  • 系统枚举
  • System.ValueType。

4
很酷,似乎是正确的答案,很好的发现!但是,System.MulticastDelegate在列表中的什么位置?
Mints97 2015年

8
@ Mints97:不知道,也许缺少文档?
蒂姆·施密特

似乎您也无法从这些类继承。
David Klempfner '19

14

根据C#4.0语言规范(编码:[10.1.5]类型参数约束),您可以了解两点:

1]类型不能为对象。因为所有类型都源自对象,所以如果允许,这样的约束将无效。

2]如果T没有主要约束或类型参数约束,则其有效基类为object。

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

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

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

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

  • 系统对象
  • 系统数组
  • 系统代理
  • 系统枚举
  • System.ValueType。

12

框架中的某些类有效地继承了从它们派生的所有类型的特殊特征,但自身不具有这些特征。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#的创造者没有想到他们的他们是非法的没有很好的理由。


10

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

主要约束

类型参数可以指定零个主约束或一个主约束。主要约束可以是引用类型,用于标识未密封的类。您不能指定以下特殊引用类型之一:System.ObjectSystem.ArraySystem.DelegateSystem.MulticastDelegateSystem.ValueTypeSystem.EnumSystem.Void。当指定引用类型约束时,您向编译器保证,指定的类型参数将是相同类型,或者是从约束类型派生的类型。


也参见:C#LS 10.1.4.1节:直接基类的类类型的不能是以下任一类型的:System.ArraySystem.DelegateSystem.MulticastDelegateSystem.Enum,或System.ValueType。此外,泛型类声明不能System.Attribute用作直接或间接基类。
Jeroen Vannevel,2015年

5

我认为,没有任何关于“特殊类” /“特殊类型”的正式定义。

您可能会考虑它们是aa类型,不能与“常规”类型的语义一起使用:

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

附言:我要添加System.Void到列表中。


2
System.Void用作通用约束=时会给出完全不同的错误=)
Mints97

@ Mints97:是的。但是,如果问题是关于“特殊”的,那么是的,void非常特殊。:)
丹尼斯

@Dennis:代码中有几个类型被限制为System.Array可以使用诸如Array.Copy将数据从一个移动到另一个的方法;参数类型受限的代码System.Delegate可以Delegate.Combine在它们上使用并将结果转换为适当的类型。有效使用通用已知类型Enum将对每个此类使用一次反射,但是通用HasAnyFlag方法可能比非通用方法快10倍。
2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.