Compiler Error CS0702

  • Thread starter Thread starter PIEBALD
  • Start date Start date
P

PIEBALD

I'd really like to be able to constrain a generic type to System.Enum or,
better, enum. But of course that results in "Compiler Error CS0702".

So far I've been checking the type parameter at runtime and throwing an
exception if the provided type is not an enum. That works, but it just
doesn't seem quite right.

After reading through section 4.4.4 of the C# 3.0 spec I see no reason why
constraining to System.Enum should not be allowed.

So my questions are:
Are there reasons why these types can't or shouldn't be supported as
constraints?
(If so, the documentation of Compiler Error CS0702 ought to state them.)
Is this simply a limitation of the compiler rather than of the language?
Are efforts being to made to support these types as constraints?
 
What is giving you the impression that the specification would allow an
Specifically, can you quote any text from the C# specification that
directly leads you to believe that you should be able to specify an enum
type as a constraint for a generic?

More the _absence_ of a restriction in that section.


According to http://msdn2.microsoft.com/en-us/library/system.enum.aspx:

"
Enum Class
Provides the base class for enumerations.
"

all enums derive from System.Enum. System.Enum _should_ satisfy section 4.4.4

"
o An implicit reference conversion (§6.1.6)
"

"
6.1.6 Implicit reference conversions
The implicit reference conversions are:
....
• From any class-type S to any class-type T, provided S is derived from T
....
"

So, it seems to me that System.Enum is a class and a particular enum derives
from it, so the language seems to allow it.

BUT, the Microsoft _implementation_ of the C# language (Visual C#) targets
..net, and in fact, the error says:

"
CS0702: Constraint cannot be special class 'System.Enum'
"

and I draw particular attention to the word "special". Of all the classes in
..net, very few are "special".

The existence of System.Enum as a base class for enums should _enable_ its
use as constraints. I see no restriction in the specification to _disallow_
it.
 
P.S. If not 6.1.6:

all enums derive from System.Enum. System.Enum _should_ satisfy section 4.4.4

"
o A boxing conversion (§6.1.7), provided that type A is a non-nullable value
type.
"

"
6.1.7 Boxing conversions
A boxing conversion permits a value-type to be implicitly converted to a
reference type. A boxing conversion exists from any non-nullable-value-type
to object, to System.ValueType and to any interface-type implemented by the
non-nullable-value-type. Furthermore an enum-type can be converted to the
type System.Enum.
"
 
my bias is towards explaining how the specification adequately describes
what you're seeing

No, the specification doesn't. Certainly the specification allows the
keywords class and struct as constraints while (regrettably) omitting enum
(and delegate and event), so perhaps one can read _that_ as a statement in
your favor. But...

From a language standpoint (not an implementation standpoint) I see no
reason to omit enum as a constraint in the first place.

If an implementer (and there _could_ conceivably be many implementations for
various platforms) chooses to implement enum as a class, then that class
_should_ be allowed as a constraint. But while Microsoft chose to use a class
to implement enums, for some unexplained reason, they also chose to make it
"special".
The fact is, you
can't constrain your type parameters to the System.Enum. You can insist
that the specification doesn't address that issue all you want, it's not
going to change how things work.

No, the specification knows nothing of System.Enum, that's merely a matter
of implementation.

The specification says that if a type has a base class you can use that base
class as a constraint. But, when implementing .net and its associated
compiler, someone decided that that shouldn't hold true for some classes even
though there appears to be no reason for that decision.
By the way, in the ECMA specification, they suggest using a static
constructor for checking the constraint, for situations where the language
doesn't support the constraint you want. That would at least ensure that
the constraint is checked only once, when you first use the class with
that specific enum type, rather than for each instance of the class you
create.

I'll take a look.
 
what you're seeing

No, the specification doesn't. Certainly the specification allows the
keywords class and struct as constraints while (regrettably) omitting enum
(and delegate and event), so perhaps one can read _that_ as a statement in
your favor. But...

Enum and delegate could indeed be useful. (In particular, I'd like to
write a generic version of Enum.Parse to avoid the casts.)

Event would be a bizarre kind of constraint though, as "event" isn't a
kind of type - it would be like having a "property" constraint. What
would such a constraint mean?
From a language standpoint (not an implementation standpoint) I see no
reason to omit enum as a constraint in the first place.

If an implementer (and there _could_ conceivably be many implementations for
various platforms) chooses to implement enum as a class, then that class
_should_ be allowed as a constraint. But while Microsoft chose to use a class
to implement enums, for some unexplained reason, they also chose to make it
"special".

The specification guarantees that there will be a System.Enum class.
From the unified 3.0 spec:

<quote>
14.4 The System.Enum type

The type System.Enum is the abstract base class of all enum types
(this is distinct and different from the underlying type of the enum
type), and the members inherited from System.Enum are available in any
enum type. A boxing conversion (§4.3.1) exists from any enum type to
System.Enum, and an unboxing conversion (§4.3.2) exists from
System.Enum to any enum type.

Note that System.Enum is not itself an enum-type. Rather, it is a
class-type from which all enum-types are derived. The type System.Enum
inherits from the type System.ValueType (§4.1.1), which, in turn,
inherits from type object. At run-time, a value of type System.Enum
can be null or a reference to a boxed value of any enum type.
No, the specification knows nothing of System.Enum, that's merely a matter
of implementation.

Um, see the above.

If the MS spec isn't good enough for you, it's section 21.4 of the
ECMA spec (4th edition).

So yes, the specification most certainly *does* know about
System.Enum.

Jon
 
Enum and delegate could indeed be useful. (In particular, I'd like to
write a generic version of Enum.Parse to avoid the casts.)

Yes, and I can only guess that support of them was left out because they
don't buy as much as class and struct do. But I see no reason why they
_can't_ be supported by an implementation. So I see no reason for the spec to
disallow them.

If, when generics were first added to the language, the spec had said that
they _are_ to be supported then they _would_ be supported. The argument that
the spec says they _aren't_ because the current implementation doesn't
support them is backward. A _language_ exists separate from any particular
_implementation_.


We can use enums as type parameters, I see no reason not to be allowed to
constrain to them. It's not as if they weren't allowed in generics at all.

I'm looking for a _conceptual_ reason why they aren't, not an
implementation-based reason and so far have seen none.

If one regards _only_ the language, not considering .net, the CLR, CTS etc.;
is there some reason why enum and delegate should _not_ be supported as
constraints?
 
Yes, and I can only guess that support of them was left out because they
don't buy as much as class and struct do. But I see no reason why they
_can't_ be supported by an implementation. So I see no reason for the spec to
disallow them.

Likewise. I really don't know why they're not supported, I'm afraid.
My previous post was mostly to correct your claim that the existence
of System.Enum was unknown to the spec ;)

Jon
 
My previous post was mostly to correct your claim that the existence
of System.Enum was unknown to the spec ;)

Yeah, I had forgotten that there are mentions of it in the Microsoft spec
(which I could regard as being "C# as implemented by the Visual C#
compiler"), and I have now downloaded the ECMA spec and checked the sections
mentioned.


I also took a quick look at the ANSI C spec, which clearly separates the
_language_ (chapter 6) from the _library_ (chapter 7), and I feel that that's
how the ECMA standard should handle C#. A _language_ shouldn't require any
particular items to exist in the _library_; much less, specify limits on how
they are to behave.

The language can say: "You must support enums, the syntax for creating one
is... and the way to access the members is..."

Then an implementer can say: "Our implementation supports enums with the
specified syntax, we support them through the use of a class named
WidgetCo.Enum, and this class also has some static methods to provide other
ways of accessing the members. (Parse, GetValues, etc.)"
 
Yeah, I had forgotten that there are mentions of it in the Microsoft spec
(which I could regard as being "C# as implemented by the Visual C#
compiler"), and I have now downloaded the ECMA spec and checked the sections
mentioned.

I also took a quick look at the ANSI C spec, which clearly separates the
_language_ (chapter 6) from the _library_ (chapter 7), and I feel that that's
how the ECMA standard should handle C#. A _language_ shouldn't require any
particular items to exist in the _library_; much less, specify limits on how
they are to behave.

I agree, but only to the extent that it remains practical. For
instance, doing this in C# would mean removing:

o lock statements
o using statements
o foreach statements
o collection initializers
o type shorthands (string for System.String, int for System.Int32 etc)

.... and no doubt others that I can't think of off the top of my head

It's important for the specification to explain exactly how those are
handled, and what the dependencies are.

The overlap between language and library/runtime isn't very large for
C#, but it *is* beneficial in my view.

Jon
 
If one regards _only_ the language, not considering .net, the CLR, CTS

So, if you are designing a _new_ language, to write software for some
_not-yet-existent_ platform, and it includes enums and generics, you would
_not_ allow enum as a constraint for a generic? Why? That's what I want to
know.
 
doing this in C# would mean removing:
o lock statements
o using statements
o foreach statements
o collection initializers
o type shorthands (string for System.String, int for System.Int32 etc)

.... and no doubt others that I can't think of off the top of my head

Well, you got me there; I can see how using, and foreach would be affected.
I'm not so convinced about lock, collection initializers, and type
shorthands, but I probably don't need to be.

(On another note; I really don't see what type shorthands add to the
language anyway other than syntactic sugar, but we needn't go there.)
 
I'm not a language designer, so knowing what I would do is sort of
pointless. I don't have the kind of knowledge and experience that would
allow me to provide a reliable answer to that question. Unless you're an
experienced language designer, I doubt you do either, for what it's worth.

OK, I won't press you further. I'll just say that _designing_ something is a
lot easier than implementing it. :)

The only language I've designed and implemented so far is a simple scripting
language for a telnet client I wrote.
 
Well, you got me there; I can see how using, and foreach would be affected.
I'm not so convinced about lock, collection initializers, and type
shorthands, but I probably don't need to be.

I might as well fill in the details for interested parties:
o lock is equivalent to a try/finally with Monitor.Enter/Monitor.Exit
o collection initializers rely on IEnumerable and Add methods
o type shorthands are specified in terms of what framework type they
represent
(On another note; I really don't see what type shorthands add to the
language anyway other than syntactic sugar, but we needn't go there.)

They're *all* just syntactic sugar. There's an awful lot of syntactic
sugar in C#, particularly in C# 3. Don't assume that "syntactic sugar"
is a bad thing - it makes the language sweeter :)

Jon
 
OK, I won't press you further. I'll just say that _designing_ something is a
lot easier than implementing it. :)

I'd disagree with that, actually. Designing a fairly general purpose
language which balances power with readability, avoids brittleness, is
unambiguous etc is a very tricky thing indeed. The C# 3 designers have
(for the most part) painted an incredibly elegant picture. The
different language features all come together for LINQ, but each is
also individually useful outside LINQ. All this without ruining the
language in other ways.

Don't forget that a mistake in one version of the language is likely
to haunt future versions. (For example, I know my view that classes
should be sealed by default is also held by at least one of the C#
language designers - but even if *all* the designers agreed on that
now, there's no way on earth they'd change it at this point - it would
cause hideous amounts of breakage.)

Designing a very simple language may indeed be easier than
implementing it - but designing a general purpose language like C# is
a different matter.

Jon
 
classes should be sealed by default

Ew, I think very few classes should be sealed; you just don't know what the
user wants to do with it.

However, I understand that things like sealed can help the compiler to
optimize the code, so I don't mind so much.

On the other hand, I think all classes should be partial by default.

Well, wait, no I _really_ think that there should be very few things that
are default.
For instance I don't think classes and members should have default access
modifiers, they should be explicit.


Well, basically I mean a sort of "ivory tower" language design (hmmm...
Pascal?), and I will admit that many real-world considerations must be made.
 
Don't assume that "syntactic sugar"
is a bad thing - it makes the language sweeter :)

Yes, but when the dessert trolley comes around and all I need is one thin
mint...
 
or I would produce a very inferior design.

Right (me too), but it would be easy. I agree that a _good_ design requires
a lot of real-world compromises.

And I certainly agree with the rest of that too; I liked C# from the time I
first read a spec of it circa 1999 (I was using C at the time).
 
Ew, I think very few classes should be sealed; you just don't know what the
user wants to do with it.

Search for "inheritance tax" on my blog: http://msmvps.com/jon.skeet

Inheritance is a powerful tool, but classes should be designed for
inheritance up front; inheritance tends to expose implementation
details (or at least make those implementation details important).
It's not so bad if none of the methods are virtual, but as soon as
they are, life becomes trickier.
However, I understand that things like sealed can help the compiler to
optimize the code, so I don't mind so much.

My reasons for preferring sealed classes have nothing to do with
optimisation, but instead with design.
On the other hand, I think all classes should be partial by default.

Why? Partial is only a compile-time notion within the same assembly. I
find it quite rare that I can create a new file, but not modify
another file contributing to the same assembly. So, the cost of making
a non-partial class partial later on is minimal.

In contrast, there's a cost to a class being partial when it doesn't
have to be. If I look at a class which is non-partial, I know
immediately that all the code for the class is in that same file;
there's no need to dig around elsewhere. If it's partial, I need to
find all the other source files before I can get a good idea of the
shape of the class.
Well, wait, no I _really_ think that there should be very few things that
are default.
For instance I don't think classes and members should have default access
modifiers, they should be explicit.

I'm in two minds about that, but I can certainly see its appeal. If we
*are* to have default access modifiers, I think C# takes exactly the
right approach by making everything as private as possible by default.
However, forcing developers to consider it to start with would also be
a good idea.
Well, basically I mean a sort of "ivory tower" language design (hmmm...
Pascal?), and I will admit that many real-world considerations must be made.

That's certainly true in many cases, but I suspect that it wouldn't
have caused too many complaints if C# had gone the "everything
explicit" route to start with.

Jon
 
OK, a different tack...

Is a type parameter constraint anything more than a compile-time type check?

The compiler can already check the type as far as being value type or
reference type, even when the type used is an enum. So compile-time type
checking of enums is possible.

namespace W
{
public enum X { X }

public class Y1<T> {}
public class Y2<T> where T : struct {}
public class Y3<T> where T : class {}

public class Z
{
object V1 = new Y1<X>() ; // this succeeds
object V2 = new Y2<X>() ; // this succeeds
object V3 = new Y3<X>() ; // this fails (as it should)
}
}

I don't see a conceptual reason why "where T : enum" _shouldn't_ be supported.
 
Back
Top