What exactly is a "Special Class"?
After failing to get something like the following to compile:
public class Gen<T> where T : System.Array
{
}
with the error
A constraint cannot be special class `System.Array'
I started wondering, what exactly is a "special class"?
People often seem to get the same kind of error when they specify System.Enum
in a generic constraint. I got the same results with System.Object
, System.Delegate
, System.MulticastDelegate
and System.ValueType
too.
Are there more of them? I cannot find any info on "special classes" in C#.
Also, what is so special about those classes that we can't use them as a generic type constraint?
Solution 1:
From the Roslyn source code, it looks like a list of hardcoded types in 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 (updated with new types) IsValidConstraintType
is Roslyn Source Browser- I've found it using a GitHub search: "A constraint cannot be special class".
Solution 2:
I found a Jon Skeet comment from 2008 on a similar question:
Why is the System.Enum
constraint not supported.
I know this is a bit off topic, but he asked Eric Lippert (the C# team) about it and they provided this answer:
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.
Solution 3:
According to MSDN it's a static list of classes:
Compiler Error CS0702
Constraint cannot be special class 'identifier' The following types may not be used as constraints:
- System.Object
- System.Array
- System.Delegate
- System.Enum
- System.ValueType.
Solution 4:
As per C# 4.0 Language Specification (Coded : [10.1.5] Type parameter constraints) tells two things:
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.
When you define a generic class, you can apply restrictions to the kinds of types that client code can use for type arguments when it instantiates your class. If client code tries to instantiate your class by using a type that is not allowed by a constraint, the result is a compile-time error. These restrictions are called constraints. Constraints are specified by using the where contextual keyword. If you want to constrain a generic type to be a reference type, use : class.
public class Gen<T> where T : class
{
}
This will prohibit the generic type from being a value type, such as int or a struct etc.
Also, Constraint cannot be special class 'identifier' The following types may not be used as constraints:
- System.Object
- System.Array
- System.Delegate
- System.Enum
- System.ValueType.
Solution 5:
There are certain classes in the Framework which effectively pass on special characteristics to all types derived from them but do not possess those characteristics themselves. The CLR itself imposes no prohibition against using those classes as constraints, but generic types constrained to them would not acquire the non-inherited characteristics the way concrete types would. The creators of C# decided that because such behavior might confuse some people, and they failed to see any usefulness to it, they should prohibit such constraints rather than allow them to behave as they do in the CLR.
If, for example, one were allowed to write: void CopyArray<T>(T dest, T source, int start, int count)
; one would be able to pass dest
and source
to methods which expect an argument of type System.Array
; further, one would get compile-time validation that dest
and source
were the compatible array types, but one would not be able to access elements of the array using the []
operator.
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.
I find the decision to outlaw most of the special constraints irksome. The only one which would have zero semantic meaning would be System.Object
[since if that were legal as a constraint, anything would satisfy it]. System.ValueType
probably wouldn't be very useful, since references of type ValueType
don't really have much in common with value types, but it might plausibly have some value in cases involving Reflection. Both System.Enum
and System.Delegate
would have some real uses, but since the creators of C# didn't think of them they're outlawed for no good reason.