2

I was testing a bit of code I wrote and was wondering if a default case handling was needed.

Lets define MyEnum like this

public enum MyEnum 
{
    [Description("Value")]
    Value = 0,
    [Description("Other value")]
    OtherValue = 1,
}

the logic I was asking myself about was similar to this one :

MyEnum val = (MyEnum)8;
if(val == MyEnum.Value)
    //do stuff
else if (val == MyEnum.OtherValue)
    //do other stuff
else 
    throw new ArgumentException("The value is not currently supported");

My reasoning was to not have a default case because the cast from a value not defined in the Enum would throw an exception anyway before even being caught by my piece of code.

So I tryed with the exemple I have up here and at my suprise the cast didn't threw any exception and it was my default case handling witch threw the exception.

My question is : Why is the cast of any int to MyEnum is valid? I understand that in this exemple the underlying value of the Enum is int but I would have expected an exception to been thrown from the cast. Why is that Valid?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Rémi
  • 3,867
  • 5
  • 28
  • 44
  • Two remarks about your code: Instead of chained `if` ... `else`, use [`switch`](http://msdn.microsoft.com/en-us/library/06tc147t.aspx). Also, there is a dedicated exception type for that kind of situations that should be thrown here, namely [`InvalidEnumArgumentException`](http://msdn.microsoft.com/en-us/library/system.componentmodel.invalidenumargumentexception%28v=vs.110%29.aspx). – O. R. Mapper Apr 11 '14 at 16:16
  • @O.R.Mapper thank you I didn't knew about the `InvalidEnumArgumentException` but about the switch I personnaly don't really like it when it come to test only a couple of value (here in my real code I'm actually testing only 3 value) – Rémi Apr 11 '14 at 16:18
  • Btw very similar question was asked before http://stackoverflow.com/questions/6413804/why-does-casting-int-to-invalid-enum-value-not-throw-exception – Sergey Berezovskiy Apr 11 '14 at 16:53

3 Answers3

4

Enumerations are just a fancy way to work with values of the underlying datatype - Int32 by default. You get named values, you lose mathematical operations. There is no rule that only values named in the declaration are valid values and therefore you get no exception or compiler error.

This becomes really obvious when you use enumerations as flags.

[Flags]
public enum Something
{
   Foo = 1,
   Bar = 2,
   Baz = 4
}

Now something like this

var x = Something.Foo | Something.Bar; // The value is 1 | 4 = 5

is absolutely fine and again you can have values you never named. It all boils down to your wrong assumption that the set of allowed values is in someway restricted and as pointed out by other answers referring to the specification this is just not the case.

Daniel Brückner
  • 59,031
  • 16
  • 99
  • 143
  • 1
    I understand that and that make sense when you define your enum with the [Flags] attribute but in this case the Enum cannot contain flag value – Rémi Apr 11 '14 at 16:20
  • @im_a_noob: Unfortunately, C# support for enums is very rudimentary and just considers the constants as aliases for arbitrary integer values. The `[Flags]` attribute is just a hint to certain methods (such as `ToString`); the C# compiler assumes every enum to be used as flags. – O. R. Mapper Apr 11 '14 at 16:22
  • The flags attribute is purely optional and only changes the behavior of `ToString()`; everything will work exactly the same way without. – Daniel Brückner Apr 11 '14 at 16:24
1

That is by design. See C# specification 4.1.9 Enumeration types:

An enumeration type is a distinct type with named constants. The set of values of the enumeration type is the same as the set of values of the underlying type. Values of the enumeration type are not restricted to the values of the named constants.

Actually enumeration is just a set of named literal fields (which are named constants of enumeration type) and one special field which holds enum instance value:

.class public auto ansi sealed MyEnum
    extends [mscorlib]System.Enum  
{
    .field public static literal valuetype Namespace.MyEnum Value = int32(0)
    .field public static literal valuetype Namespace.MyEnum OtherValue = int32(1)
    .field public specialname rtspecialname int32 value__
}

As you can see this is a simple filed of underlying type, and any Int32 value can be assigned to enum variable.

Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
0

The spec describes this behaviour:

14.5 Enum values and operations

The set of values that an enum type can take on is not limited by its enum members. In particular, any value of the underlying type of an enum can be cast to the enum type, and is a distinct valid value of that enum type.

Lee
  • 142,018
  • 20
  • 234
  • 287