Those classes were already different before generic constraints, or even generics, were added to the .NET framework and support for them added to the C# language.
What each of them have in common, is that inheriting from them is different than with other types:
System.Object
: You can't not inherit from this in C#.
System.Array
: You inherit from this by creating an array of an existing type (Array x = new int[2];
etc.)
System.Delegate
: You inherit from this by creating a delegate
(which then derives from MulticastDelegate
, also a "special type", which derives from Delegate
).
System.Enum
: You inherit from this by creating an enum
.
System.ValueType
: You inherit from this by creating a struct
.
Now, note that aside from new()
generic constraints are all about inheritance or implementation of interfaces (which is akin to inheritance in many ways). Indeed the other restrictions are that you can't use a pointer type, and you can't use a sealed type; two cases where you can't have a derived type anyway (though the the ban on sealed types is primarily because you are likely creating a generic type or method when you don't need too, and is an attempt to protect you from yourself).
And as such code that is based on inheritance features (as constraints are) when faced with special cases about inheritance will likely have to itself involve special cases. Those special cases were dealt with in the simplest way: By prohibiting them.
The value is also less in many of these cases:
System.Object
: Since the only types that can't be converted to System.Object
are pointer types, and these can't be used as generic parameters anyway, any such constraint is redundant.
System.Array
: You can define in terms of element types: void DoSomethingWithArray<T>(T[] array)
etc.
System.Delegate
: Such would be useful, though in many cases we can define in terms of parameter and/or return types, but there are cases this doesn't catch.
System.Enum
: Would be useful.
System.ValueType
: Already dealt with; constrain as struct
. Conversely we can also constrain as class
to exclude this case so we've actually a "not inherited from…" option we don't have otherwise.
This is not to deny that being able to constrain in terms of Delegate
, MulticastDelegate
or Enum
would not be useful (probably most so we Enum
), but in terms of justifying the extra work to cover these types the others would give little or no benefit, so the benefit of less restrictions is reduced.
Enum
. By this restriction, writing a generic method with anEnum
constraint is impossible unless one wants to use workarounds like Jon Skeet used in Unconstrained Melody. Admittedly, not everyone has the skill to do these workarounds even when they have legitimate reasons for wanting to use any of these "Special Classes" as constraints. – ValuatorValueType
is a reference type but all of the actual instances that derive from it are value types. – Cephalic