开发者

Why can't I declare an enum inheriting from Byte but I can from byte?

If I declare an enum like this ...

public enum MyEnum : byte {
    Val1,
    Val2
}

... it's working.

If I declare an enum like this ...

public enum MyEnum : System.Byte {
    Val1,
    Val2
}

... it's not working. The compiler throw :

error CS1008: Type byte, sbyte, short, ushort,开发者_如何学Go int, uint, long, or ulong expected

As byte is an alias to the actual type, System.Byte, why can't I use the second declaration ?


There are a number of questions raised here.

Why can't I declare an enum inheriting from Byte but I can from byte?

As others have noted, that is what the specification says.

The language design committee notes do not justify this decision. The notes from June of 2000 say


The keywords for the predefined types (e.g., int) and their corresponding type names (e.g., Int32) can mostly but not entirely be used interchangeably. We discussed whether we wanted to make any changes in this area. We didn’t.

Here is the list of places where they are not interchangeable.

  • "int" can be used as an underlying type for enums; Int32 can't.
  • Alias declarations can't use keywords.

Some musings of mine on the subject:

The first thing that comes to mind is that every time you give a user a choice, you give them an opportunity to write a bug, and every time you give them an opportunity to write a bug you have to make an error message for it. If we allowed "enum X : Byte" then we have give a sensible error message when the user has accidentally erased the "using System;". We can avoid that potential confusion and all the costs of developing and testing an error-reporting heuristic by simply not allowing the choice in the first place.

The second thing that comes to mind is that the underlying type of an enum is fundamentally about the mechanism of the enum, not its meaning. It therefore seems plausible that the underlying type clause should be limited to things that require no semantic analysis; we know that the underlying type is one of eight possible types, so let's just have the user mechanically choose from one of those eight types unambiguously.

The third thing that comes to mind is that the error analysis can be performed during syntactic analysis rather than semantic analysis. The earlier the error is caught, the better.

UPDATE: I just asked one of the people who was in the room that day whether there was anything that I missed in my musings and he said that yeah, they thought about it and decided that doing the full type analysis was work for the development, test and maintenance team, work which bought the user nothing of value. (He also noted that they had similar arguments over System.Void; should it be legal to say "public static System.Void Main(string[] args)" for example? Again, they decided that this added no value for users but did add potential ambiguity and work for the team.)


Why is "char" not a legal underlying type?

Again, that's what the spec says. Again, the language design notes from October 1999 are no help in determining why:


The unsigned integral types may be used as the underlying types for enums. The only integral type that cannot be used is char.


Again, we can guess. My guess would be that enums are intended to be fancy numbers. Chars are in practice integers as an implementation detail, but logically they are not numbers, they are characters. We want to be able to do operations on enums like addition, "or-ing" and "and-ing" flags, and so on; the spec is clear that these operations are done as though they were being done on the underlying type. The char type, not logically being a number, does not define all the operators you might want. And finally, if you want a two-byte enum value then you already have short and ushort at your disposal.


A related question from an email about this question:

A careful reading of the grammar specification says that 'char' is grammatically a legal underlying type, but the paragraph of explanatory text after the grammar says that 'char' is not a legal underlying type. Is the spec inconsistent?

Yes. I'm not losing sleep over it. If it makes you feel better, imagine that the grammar line that says

enum-base : integral-type

instead reads

enum-base : integral-type (but not char)


Well, it's according to the specification (§14.1). The grammar specifies that the production for enum-declaration is

enum-declaration:
    attributes_opt   enum-modifiers_opt   enum   identifier   enum-base_opt   enum-body   ;_opt

where

enum-base is

:integral-type

and

integral-type:
sbyte
byte
short
ushort
int
uint
long
ulong
char

As for the why the specification is this way, it is not clear.

Note that char is listed as a terminal for integral-type but the specification explicitly states that

Note that char cannot be used as an underlying type.

By the way, I really think best practice here is to use the aliases. I only use the .NET name instead of the C# keyword for these primitive types (and string) when I want to invoke a static method. So

Int32.TryParse

instead of

int.TryParse.

Otherwise, I say, e.g., typeof(int) and not typeof(Int32).


The simplest answer is "that's just the way it's specified." (It's in section 14.1 of the C# 4 spec. Numbering may vary for other versions.)

The spec doesn't give any particular reason for this1, but it's arguably easier to describe the integral type alias names (which are all keywords) than to say it can be any type which resolves (via other type aliasing such as using directives) to one of a particular set.

Do you have any reason for wanting to use System.Byte instead?


1 The annotated C# 4 spec does have one annotation in this section... but it was written by me, and just points out that this is one of the few places in the spec where you really can't replace the aliases with normal type names. So not very helpful for this question :)


Just for information:

With new compiler Roslyn this code

enum SomeEnum : System.Int32
{
    None,
    Some
}

is valid, so, now for enum base types can be used not only aliases, but full types.

Sample on dotnetfiddle, where you can select compiler: https://dotnetfiddle.net/oXyAgV


Here is the answer, at least partially, from MSDN:

Every enumeration type has an underlying type, which can be any integral type except char. The default underlying type of enumeration elements is int. To declare an enum of another integral type, such as byte, use a colon after the identifier followed by the type, as shown in the following example.

Copyenum Days : byte {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};

The approved types for an enum are byte, sbyte, short, ushort, int, uint, long, or ulong.


Looks like it does not accept actual type names, but language keywords only.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜