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?
> 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.
I guess I'm a bit confused about the relationship between C# version and
specification version.
I'm looking at "Standard ECMA-334", 4th edition, June 2006. There's no
"section 4.4.4". The document I'm looking at describes generic
constraints on page 405, section 25.7. And it describes the allowable
constraints to clearly exclude enums or other value types. The only
primary constraints allowed are an actual reference (class) type, or the
keywords "class" or "struct".
What is giving you the impression that the specification would allow an
enum as a type constraint?
> So my questions are:
> Are there reasons why these types can't or shouldn't be supported as
> constraints?
Well, I think the most obvious reason is that since value types can't
inherit other value types, if you're constraining the class to allow only
one specific value type, then there's no need for the class to be generic
in the first place. You'd only ever be able to use that generic class
with one specific type.
Since an enum is a value type, it would fall into that same reasoning.
> (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?
See above. As I read it, this is clearly described as part of thelanguage.
> Are efforts being to made to support these types as constraints?
Not to my knowledge. I don't see why any effort would be made, given the
above.
Pete
> [...]
>> 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.
>
> I guess I'm a bit confused about the relationship between C# version and
> specification version.
>
> I'm looking at "Standard ECMA-334", 4th edition, June 2006. There's no
> "section 4.4.4". The document I'm looking at describes generic
> constraints on page 405, section 25.7. And it describes the allowable
> constraints to clearly exclude enums or other value types. The only
> primary constraints allowed are an actual reference (class) type, or the
> keywords "class" or "struct".
Okay...I found the C# 3.0 specification. I guess ECMA hasn't adopted it
yet? I don't see it anywhere on their web site.
Anyway, section 4.4.4 has to do with satistfying constraints when _using_
a generic. I don't see how that's relevant with respect to what
constraints you can declare for a generic.
Which brings me back to my previous question:
> What is giving you the impression that the specification would allow an
> enum as a type constraint?
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?
Pete
I'm looking at the "C# Language Specification Version 3.0" available at
http://msdn2.microsoft.com/en-us/vcsharp/aa336809.aspx
> 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". [...] Are there reasons why these types can't or shouldn't be
> supported as constraints?
This is basically because enum types aren't really inherited from the
base types, despite the colon notation. For example, if you write "enum
X : byte", it means that every member of X *is* a byte and not that X is
anything like a class derived from byte. (If you try to derive a class
from byte, you'll see that you can't possibly do so: it's a sealed
class.)
> 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.
An alternative would be to require a type "where T : MyEnumWrapper", and
have MyEnumWrapper be a wrapper for a single value of that particular
enum. Ugly, certainly, but you could have compile-time type safety if
you did that.
Eq.
> "PIEBALD" <PIE...@discussions.microsoft.com> wrote:
>
>> 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". [...] Are there reasons why these types can't or shouldn't be
>> supported as constraints?
>
> This is basically because enum types aren't really inherited from the
> base types, despite the colon notation. For example, if you write "enum
> X : byte", it means that every member of X *is* a byte and not that X is
> anything like a class derived from byte. (If you try to derive a class
> from byte, you'll see that you can't possibly do so: it's a sealed
> class.)
"byte" isn't a class at all, never mind a sealed one. It's a struct
(thus, a value type):
http://msdn2.microsoft.com/en-us/system.byte.aspx
Likewise, an enum is a value type.
>> 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.
>
> An alternative would be to require a type "where T : MyEnumWrapper", and
> have MyEnumWrapper be a wrapper for a single value of that particular
> enum. Ugly, certainly, but you could have compile-time type safety if
> you did that.
Even more so than a generic constrained to just a single type, I don't see
what the value in a generic constrained to a single value of a single type
would be.
Pete
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.
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.
"
> [...]
> and I draw particular attention to the word "special". Of all the
> classes in
> .net, very few are "special".
That's correct. And the "special" is IMHO important. They are telling
you that it's not really a class. You can't inherit it, for example.
It's a façade, one that exists to make particular aspects of the type more
seamless. The type isn't _really_ a class.
An enum is still basically a value type...that is, it behaves more like a
value type than a reference type, and it's reference types that can be
used as a constraint.
> 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.
Well, this seems like a classic example of how biases affect our reading
of information. You want to constrain the generic to only support enums,
so of course you're predisposed to not read the information in a way that
would contradict that. On the other hand, my bias is towards explaining
how the specification adequately describes what you're seeing, so I'm
predisposed to reading the information in a way that does contradict your
position.
Sorry to say, there's really not much way around that. 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.
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.
Of course, if you're trying to constrain something other than a class, I
guess that wouldn't apply. :)
Pete
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.
> "byte" isn't a class at all, never mind a sealed one. It's a struct
> (thus, a value type):
Yes, I meant a "sealed type" (see compiler error when trying to
inherit).
> Even more so than a generic constrained to just a single type, I don't
> see what the value in a generic constrained to a single value of a
> single type would be.
Because you can't constrain to an enum type but you can to a reference
type?
Eq.
>> my bias is towards explaining how the specification adequately describes
> what you're seeing
>
> No, the specification doesn't.
As I already wrote, obviously you're biased to take that view. But other
than that, I don't see any justification for an absolute statement like
that.
> 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.
Indeed, I would. Why not?
> But...
>
> From a language standpoint (not an implementation standpoint) I see no
> reason to omit enum as a constraint in the first place.
There's nothing in the specification that leads me to believe that you
should be able to use the special System.Enum type as a constraint.
System.Enum, in spite of being called a class, inherits from ValueType.
It's clearly _not_ a normal class, and isn't really a reference type. I
find the C# spec pretty clear on their intent that only reference types
should be usable as specific types for a constraint.
It gets a bit confusing in the sense that the System.Enum type _is_ a
valid type to use to _satisfy_ a contraint of "class". However, note that
_only_ System.Enum is a valid type; specifically, types that inherit
System.Enum are _not_. Those satisfy only "struct" constraints. So even
though at first glance, that appears to be a counter-example to excluding
System.Enum as a constraint, it's not.
Just because System.Enum is itself a legal reference type, that doesn't
mean that types that inherit it are, or that it's useful as a base class
for constraining. Value types (of which actual enums are included) are
treated very differently in generics than reference types, and the
inheritance rules that work for reference types just aren't applicable for
the value types.
For the same reason that value types that inherit System.Object don't
satisfy requirements that specify System.Object (thus, boxing occurs, to
create something that does), value types that inherit System.Enum also
don't. That means that the only class that could ever satisfy a
constraint of System.Enum would be System.Enum itself. And since generics
that can be used with only one type don't make any sense, the compiler
doesn't allow that.
> If an implementer (and there _could_ conceivably be many implementations
> for
> various platforms) chooses to implement enum as a class,
System.Enum is a very special type, as are enums. Importantly, declared
enums are always value types. An implementer doesn't have a choice in the
matter, and can't implement enums as a reference type.
> 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".
I don't think the reasons are unexplained.
>> 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.
Huh? The C# specification specifically references System.Enum, as one of
the two base types for an "enum-type". System.Enum is referred to
frequently in the specification. That's quite the opposite from "the
specification knows nothing of System.Enum".
It even specifically says "The direct base class of a class type must not
be any of the following types: ...System.Enum". This rules out the
possibility of an enum ever being a reference type.
More directly for your question, section 10.1.5 reads in part: "A
class-type constraint must satisfy the following rules:..." and then one
of the rules is "The type must not be one of the following types:
...System.Enum"
In other words, the compiler error you're seeing is specifically called
out by the specification as being required.
> 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.
Well, I disagree that there's no reason. There are inferential reasons of
consistency, but more importantly the specification specifically says that
what you want isn't allowed. This isn't an implementation detail. It's
directly and specifically addressed in the specification.
Pete
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.
</quote>
> > 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.
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
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?
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
>> 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.
You are arguing the same reasoning that you say shouldn't be argued. You
are using what's possible in the implementation as justification for what
should exist in the specification. That's just as incorrect as arguing
that the specification is a certain way because of a specific
implementation.
You are right when you say that the language needs to be designed
independently of an implementation. This includes hypothetical
implementations. Just because something is possible does not make it
desirable in a clean, user-friendly language design.
> 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.
No one has argued that though.
I have pointed out reasons why it might make sense for System.Enum to not
be a valid constraint, _independent_ of the fact that the spec
specifically disallows it (which you have for some time asserted wasn't
the case...I assume you now understand your assertion was wrong, even
though you haven't explicitly acknowledged that mistake).
I have also pointed out that the specification does specifically disallow
the use of System.Enum as a constraint, contrary to your statements that
it doesn't.
At no point have I said, or even suggested, that the specification
disallows the use of System.Enum as a constraint _because_ of the
implementation.
> A _language_ exists separate from any particular _implementation_.
No one is saying it doesn't.
> 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 would have some sympathy for you if you saw the reason but disagreed
with it, or felt it wasn't significant enough to justify not supporting
the behavior you want.
But at this point, for you to continue saying that you see _no_ reason at
all, that's just being hard-headed as far as I'm concerned.
> I'm looking for a _conceptual_ reason why they aren't, not an
> implementation-based reason and so far have seen none.
For me, the most obvious reason is that even though System.Enum is a
class, actual enums don't really inherit it as a class, in the same way
that value types don't really inherit System.Object as a class.
For example, if class B inherits class A, then when you write:
A a = new B();
The variable "a" contains exactly what was returned by the "new B()". But
that's not how it works for System.Enum. As is the case for any value
type, assigning an instance of the value type to a reference type variable
requires boxing. So when you write:
System.Enum a = MyEnum.Value1;
a whole new instance of System.Enum is automatically created on your
behalf, and a reference to that instance is assigned to the variable "a".
Value types, in spite of some semantic sleight-of-hand, don't _really_
inherit classes the same way that reference types do.
Additionally, generics treat value type parameters differently from
reference type parameters. One particular difference is that for
reference type parameters, a single implementation of the generic can be
shared among all concrete uses of the generic. But for value types, a
whole new instance is created for each different value type used with the
generic.
For better or worse, the C# language specification treats value types
differently than it treats reference (class) types. This difference in
treatment extends to not allowing a value type to satisfy a reference type
constraint in a generic. Since no value type can satisfy the reference
type constraint of System.Enum, that means no actual declared enum could
do so (since they are always value types) and so it would be pointless for
System.Enum to be allowed as a constraint.
Now, it's true: value types can implement _interfaces_ and an interface is
a valid constraint for a generic. So the language designers _could_ have
treated System.Enum as more of an interface, which would then allow for it
to be a useful constraint.
But, they didn't. Nor is it clear to me that they easily could have,
unless they had anticipated generics during the initial design of C# when
enums were introduced. There are other reasons that it makes more sense
to treat these special classes as actual classes than as interfaces,
related to the fact that they bring an implementation with them.
Interfaces don't provide implementations, but classes do. Having special
interfaces such as System.Object, System.Enum, and System.ValueType that
include their own implementations would have been at least as awkward as
not allowing System.Enum as a constraint for a generic.
So, instead they are special classes, not special interfaces, and with
that definition carries the specific limitation within generics that they
aren't useful as a constraint because a type that could satisfy them as a
constraint is disallowed for other reasons (specifically, that value types
aren't allowed to satisfy reference type constraints).
None of the above is unavoidable. It's true, the languages designers
could have taken different routes to accomplish similar goals, and in
doing so enabled what you're arguing for. But they didn't, and the fact
is that design is always about compromise. This includes the design of a
language. Inasmuch as the designers _chose_ not to support this, I'm sure
they did so in order to preserve some aspect of the language that was more
important to them, such as those things I mention above.
> 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.
Pete
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.)"
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
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.
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.)
> 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.
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.
Also, your question is too vague. It doesn't provide any other specifics
about the language that might constrain this particular area of the
design. In particular, the current state of C# appears to me to be a
consequence of wanting to support both reference types and value types,
along with wanting to support enums as value types. Change either of
those design goals, and what's possible and/or desirable in generics
changes as well.
Personally, I feel that both of those goals are important. Much more
important than the goal of supporting a special class like System.Enum as
a constraint in a generic. The utility of the former goals is as broad as
the utility of the latter is not.
As for why one would not support System.Enum as a constraint for a generic
given those stated goals, please read the message I already posted, as it
already describes my thinking on the matter. There's no need to repeat
myself.
Pete
The only language I've designed and implemented so far is a simple scripting
language for a telnet client I wrote.
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
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
> OK, I won't press you further. I'll just say that _designing_ something
> is a
> lot easier than implementing it. :)
Yikes. Like Jon, I completely disagree with that. I have every
confidence that, given a correct design provided to me, I can implement
whatever that design might be. However, producing a correct design is
much more challenging.
I have no doubt that, if I wanted to, I could write a C# compiler. Today,
with the skills I have now. It might not be the best compiler around
(okay, it probably wouldn't be :) ), but it would work and it would comply
with the design.
But I am definitely not experienced enough to be designing an actual
language. The skills I currently have are inadequate to the task, and I
would either spend a huge amount of time (years) learning what I need to
know (either by trial and error, or through study of practices of other
language designers), or I would produce a very inferior design.
Frankly, having used a wide variety of languages over my career as a
programmer, I'm very impressed with C# even today (that is, now that the
honeymoon is over, so to speak :) ). I admit, I was a little put off
initially by its similarity to VB, but using C# gave me a greater
appreciation for some of the design choices made in VB, and I really like
how C# just "feels" simpler and more precise. Where it restricts the
programmer, it does so in ways that avoid too much inconvenience while
helping to prevent relatively common-but-serious bugs.
I don't think just anyone could come up with a language design that
provides all that. It's clear that some very smart people, looking very
hard at a wide variety of existing languages and their positive and
negative characteristics, came up with C# and while they may not always
have compromised in exactly the way everyone else would have, I always
feel confident that they made a particular compromise with the best
intentions and for good reasons. I've yet to see a counter-example to
that impression.
Pete
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.
Yes, but when the dessert trolley comes around and all I need is one thin
mint...
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).
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
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.
I suspect it's also checked at runtime.
> 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.
Absolutely.
> I don't see a conceptual reason why "where T : enum" _shouldn't_ be supported.
Likewise. I don't think any of us have come up with a reason why it
shouldn't be supported. I'd be interested to know the reason too - but
I don't think you'll find it here ;)
Jon
> OK, a different tack...
You are approaching the definition of "insane".
> Is a type parameter constraint anything more than a compile-time type
> check?
No. That's the point of generics...it's all about what can be verified at
compile-time.
> 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.
No one said it wasn't. The issue isn't what's possible. The issue is
what's consistent and workable in the context of the other rules of C#.
> [...]
> I don't see a conceptual reason why "where T : enum" _shouldn't_ be
> supported.
Why not? Your example has nothing to do with the question, and Jon and I
have both already provided quite a lot of detail regarding the question.
Pete
> [...]
>> I don't see a conceptual reason why "where T : enum" _shouldn't_ be
>> supported.
>
> Likewise. I don't think any of us have come up with a reason why it
> shouldn't be supported. I'd be interested to know the reason too - but
> I don't think you'll find it here ;)
I'm surprised to see you write that. I admit that I haven't provided a
proof per se. But I think that the general issues surrounding the lack of
inheritance for value types and the desire for enums to be value types are
certainly relevant.
I agree that the compiler could support "enum" as a constraint, but it
would carry some implications for existing generic rules that would have
to be changed to allow that. There may in fact be good reasons for those
rules that carry greater weight than the benefit of supporting "enum" as a
constraint.
At the very least, I see enough evidence to grant the language designers
the benefit of the doubt.
Pete
It's quite possible that I haven't been reading your posts closely
enough. I'm away from home at the moment, so don't have the benefit of
my preferred newsreader :(
I quite agree with your point that there's no reason to expect it to
work within the current C# specification, but to my mind the question
of exactly why C# is specified that way is still an open one.
The idea of only assessing the validity of a type argument at
execution time (with a static constructor) is a grotty workaround,
IMO.
> I agree that the compiler could support "enum" as a constraint, but it
> would carry some implications for existing generic rules that would have
> to be changed to allow that. There may in fact be good reasons for those
> rules that carry greater weight than the benefit of supporting "enum" as a
> constraint.
There may well be - but I'd like to hear them rather than assuming
they exist.
> At the very least, I see enough evidence to grant the language designers
> the benefit of the doubt.
I suspect there may well be very good reasons - but I just haven't
seen anything which convinces me yet.
When I get back home I may drop a line to the team to see if they'd be
willing to comment on it...
Jon
Thanks for mentioning that again.
I didn't see where it was mentioned in the spec, but your mention of it
prompted me to realize that the class I'm working on should be static.
<snip>
> > I don't see a conceptual reason why "where T : enum" _shouldn't_ be supported.
>
> Likewise. I don't think any of us have come up with a reason why it
> shouldn't be supported. I'd be interested to know the reason too - but
> I don't think you'll find it here ;)
I asked the C# team about this issue, and got this reply:
<quote>
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=3Fd 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.
</quote>
--
Jon Skeet - <sk...@pobox.com>
Web site: http://www.pobox.com/~skeet
Blog: http://www.msmvps.com/jon.skeet
C# in Depth: http://csharpindepth.com
That's what I thought. I'll keep my fingers crossed. Thanks for your effort.