Why Switch for Enum Accepts Implicit Conversion to 0 But No for Any Other Integer

Why switch for enum accepts implicit conversion to 0 but no for any other integer?

From ECMA-334 (C# Language Specification)

13.1.3 Implicit enumeration conversions

An implicit enumeration conversion permits the decimal-integer-literal
0 to be converted to any enum-type.

enum's default value is 0 and at compile time it is known that is why it is allowed in the switch statement. For value other than 0, it can't be determine at compile time whether this value will exist in the enum or not.

enum (C# Reference)

Assigning additional values new versions of enums, or changing the
values of the enum members in a new version, can cause problems for
dependant source code. It is often the case that enum values are
used in switch statements, and if additional elements have been added
to the enum type, the test for default values can return true
unexpectedly.

C# enum - why does *implicit* casting from 0 work?

It's this way because that's what the spec says...

This is another reason why it's always a good idea to give all your enums an item with value 0, 'cos you're likely to get them with that value sometimes.


The appropriate section in the C# language spec 6.1.3:

6.1.3 Implicit enumeration conversions

An implicit enumeration conversion permits the decimal-integer-literal 0 to be converted to any enum-type and to any nullable-type whose underlying type is an enum-type. In the latter case the conversion is evaluated by converting to the underlying enum-type and wrapping the result (§4.1.10).

As to why it's that way - well, I guess only someone on the language committee that decides these things would know.

In fact, we do have something like that if you look at rawling's comment to the original question.

Why does C# 3 allow the implicit conversion of literal zero (0) to any Enum?

C# has always allowed the implicit conversion of the literal 0 to any Enum value. What has changed is how the rule is applied to other constant expressions. It was made to be more consistent and allow any constant expressions which evaluates to 0 to be implicitly convertible to an enum.

The example you gave produces the same behavior in all versions of C#.

Here is an example of changed behavior (directly from the linked documentation)

public enum E
{
Zero = 0,
One = 1,
}

class A
{
public static A(string s, object o)
{ System.Console.WriteLine("{0} => A(object)", s); }

public static A(string s, E e)
{ System.Console.WriteLine("{0} => A(Enum E)", s); }

static void Main()
{
A a1 = new A("0", 0);
A a3 = new A("(int) E.Zero", (int) E.Zero);
}
}

Visual C# 2005 output:


0 => A(Enum E)
(int) E.Zero => A(object)

Visual C# 2008 output:


0 => A(Enum E)
(int) E.Zero => A(Enum E)

Why can't I switch on a class with a single implicit conversion to an enum

The language design notes archive does not provide a justification for this decision. This is unfortunate, since the decision was changed. As you can see, the design evolved over time:

Notes from May 26th, 1999:

What types are allowed in as the
argument to a switch statement?
integral types including char, enum
types, bool. C# also permits types
that can be implicitly and
unambiguously converted to one of the
aforementioned types. (If there are
multiple implicit conversion, then its
ambiguous and a compile-time error
occurs.) We're not sure whether we
want to support string or not.

June 7th, 1999:

We discussed enabling switch on string
arguments. We think this is a good
feature – the language can add value
by making this common case easier to
write, and the additional complexity
for the user is very low.

December 20th, 1999:

It is illegal to switch on an
expression of type bool. It is legal
to switch on an expression of an
integral type or string type. It is
legal to switch on an expression of a
type that has exactly one implicit
conversion to an integral type or
string type.

Here we have the first occurence of the rule in question. Enums seem to have disappeared. And why not use user-defined implicit conversions to enum? Was this simply an oversight? The designers did not record their thoughts.

Note that the first sentence is NOT what we implemented. It is unclear to me why the implementors did the opposite of what the design committee recommended. This comes up again in the notes several years later:

August 13, 2003:

The compiler allows switch on bool.
Don’t want to document this and add it
to the language. Don’t want to remove
it for compatibility reasons. Decided
to silently continue to support switch
on bool.

I decided that this was silly; when we produced the annotated print edition of the C# 3.0 specification, I added bool (and bool?) to the list of legal governing types.

In short: the whole thing is a bit of a mess. I have no idea why enums were in, then out, then half-in-half-out. This might have to remain one of the Mysteries of the Unknown.

Implicit conversion from int to enum class in switch statement

The standard Clause 4, "standard conversions", only every lists unscoped enumerations. Therefore, strong enums do not have any standard conversions, and you must use the static_cast in either direction.

You could argue that this sort of explicitness is the entire point of the strong enums. They do not act as integers at the drop of a hat, but rather require explicit declaration of intent. Note [thanks, @DyP] that switch statements explicitly support strong enums and do not require a manual conversion to some integral type.

why can enum class values of type int not be used as int

That's the feature. Scoped enumerations are not implicitly convertible to integers, so you can't use them in lieu of integers. They are strongly typed by design. If you want the implicit conversion, use unscoped enumerations.

enum MsgType : int {
Output = 1,
Input = 2,
Debug = 3
};
enum MsgSender : int {
A = 1,
B = 2,
C = 3
};

Scoping and the specification of an underlying type are orthogonal.

Alternatively, if you only want some operations to be defined, while the enumerations remain strongly typed in general, you can overload the appropriate operators to accomplish that

int operator<<(MsgType, int); // etc

But if I would not want them to be convertible, why would i specify the type to be int

To ensure a certain layout. To follow a specific ABI. To allow forward declaring the type.

Can I avoid casting an enum value when I try to use or return it?

enums are supposed to be type safe. I think they didn't make them implicitly castable to discourage other uses. Although the framework allows you to assign a constant value to them, you should reconsider your intent. If you primarily use the enum for storing constant values, consider using a static class:

public static class ReturnValue
{
public const int Success = 0;
public const int FailReason1 = 1;
public const int FailReason2 = 2;
//Etc...
}

That lets you do this.

public static int main(string[] args){
return ReturnValue.Success;
}

EDIT

When you do want to provide values to an enum is when you want to combine them. See the below example:

[Flags] // indicates bitwise operations occur on this enum
public enum DaysOfWeek : byte // byte type to limit size
{
Sunday = 1,
Monday = 2,
Tuesday = 4,
Wednesday = 8,
Thursday = 16,
Friday = 32,
Saturday = 64,
Weekend = Sunday | Saturday,
Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday
}

This enum can then be consumed by using bitwise math. See the below example for some applications.

public static class DaysOfWeekEvaluator
{
public static bool IsWeekends(DaysOfWeek days)
{
return (days & DaysOfWeek.Weekend) == DaysOfWeek.Weekend;
}

public static bool IsAllWeekdays(DaysOfWeek days)
{
return (days & DaysOfWeek.Weekdays) == DaysOfWeek.Weekdays;
}

public static bool HasWeekdays(DaysOfWeek days)
{
return ((int) (days & DaysOfWeek.Weekdays)) > 0;
}

public static bool HasWeekendDays(DaysOfWeek days)
{
return ((int) (days & DaysOfWeek.Weekend)) > 0;
}
}

Why enums require an explicit cast to int type?

There are two primary and inconsistent uses of enums:

enum Medals
{ Gold, Silver, Bronze }

[Flags]
enum FilePermissionFlags
{
CanRead = 0x01,
CanWrite = 0x02,
CanDelete = 0x04
}

In the first case, it makes no sense to treat these things as numbers. The fact that they are stored as integers is an implementation detail. You can't logically add, subtract, multiply or divide Gold, Silver and Bronze.

In the second case, it also makes no sense to treat these things as numbers. You can't add, subtract, multiply or divide them. The only sensible operations are bitwise operations.

Enums are lousy numbers, so you should not be able to treat them as numbers accidentally.



Related Topics



Leave a reply



Submit