More reasons never to use C# enums

I already hate c# enums, for reasons I have expounded on before. But I just found another doozy. Suppose I have an enum defined as follows:

public enum AnEnum
{
  One = 1,
  Two = 2
}

What do you think would happen if I tried to do this:

int illegalValue = 666;

AnEnum shouldBeIllegal = (AnEnum)illegalValue;

Console.WriteLine(string.Format("Value is : {0}", shouldBeIllegal));
Console.WriteLine(string.Format("Type is : {0}", shouldBeIllegal.GetType()));

I would expect an Exception of some sort - probably some kind of cast exception.So what happens? It works! C# and .NET will let you create a “strongly typed” enum with an underlying int value of 666, or anything you like.. Now that’s just insane. It also gives me another reason to prefer the Java style strongly typed enums.

Here’s the complete code I used to prove this:

using System;
using NUnit.Framework;
namespace EnumIsBroken{
  public enum AnEnum{One = 1,Two = 2}

  [TestFixture]
  public class EnumTests{
    [Test]
    public void BrokenEnumBehaviour()
    {
      int illegalValue = 666;
      AnEnum shouldBeIllegal = (AnEnum)illegalValue;
      Console.WriteLine(string.Format("Value is : {0}", shouldBeIllegal));
      Console.WriteLine(string.Format("Type is : {0}", shouldBeIllegal.GetType()));
    }

    [Test]
    public void NormalBehaviour()
    {
      AnEnum one = (AnEnum)1;
      Assertion.AssertEquals(AnEnum.One, one);
    }
  }
}

3 Responses to “More reasons never to use C# enums”

  1. Michael Goldobin Says:

    With the C# 3.0 it is possible to extend it with TryParse and do it nicer than Microsoft :) http://lazyloading.blogspot.com/2008/04/enumtryparse-with-net-35-extension.html

  2. stEvil Says:

    1. Thats why you use the enums values names “ie one, two” not explicit numeric values.

    2. Operations using enums should use if or switch statements. When using a switch statement use a default case, and use it to either provide a default value, or throw an exception.

    3. If the value MUST be within a range, make the enum private, instantiate it in a class, and provide a property with limiting values.

    Enums aren’t a problem, hacks writing “fisher-price” code are

  3. Redsplinter Says:

    bump
    soapbox
    AHA, anon posting! Somewhere I can moan about Microsoft’s bad design choices without getting spam.

    @Chris: Beer is the answer ;)
    @Michael: Excellent link. I try to steer away from the out keyword though. Old nightmares from expressions like “void** foo = var->warg(void*, &args, count)”. Psychological block I’m trying to work through >.<
    @stEvil: Really now? Fisher price?

    Enums are one of my main beefs with C#. To have such a strongly typed language allow casting willy nilly as above is both blasphemic and hypocritical.
    I don’t understand why it would have killed them to have the equivalent of:

    “if( Enum.IsDefined(typeof(MyEnum), value) ) {
    return (MyEnum)Enum.ToObject(typeof(MyEnum), value);
    }
    throw new InvalidCastException(”Destination type contains no definition for value ” + value);”

    _somewhere_ in their cast.

    I’m creating a toolkit that has many configurable values that are dropped in a combo and/or are configured in text by the user, as they prefer. For the convenience of all parties, the best way to do this is aliasing these values in an enum that can convert from a textual description or numeric value in a type safe way.

    At least extension methods make it a bit less painful:
    public static T toEnum(this long/int/short source)…
    public static Enum toEnum( this Type source, long/int/short value )…
    public static Enum toEnumFromDesc( this Type source, Predicate predicate )…
    public static T toEnumFromDesc(this string source)…

    (I didn’t say less clunky, just less painful. And yes the guts of those get kinda strange and use custom attributes.)

    I mean seriously, the enums along with unchecked exceptions, no fallthrough switches, goto, etc. make me want to start a Java mutiny.

    pine
    SampleRate rate = (SampleRate)666; //throws error
    /pine
    /soapbox
    /bump

Leave a Reply