Mobile Software Engineering

2008-10-14 by tamberg

Avoiding C# Enums

While the designers of C# introduced them with good intentions, enums also add a surprising amount of complexity to the language. And some serious sources of errors. The C# Specification states that:

An enum type declaration defines a type name for a related group of symbolic constants. [...] a runtime decision is made from a fixed number of choices that are known at compile-time.[...] The use of enums makes the code more readable and self-documenting.

But in a system with more than one assembly, there is not necessarily a single time of compilation. As a result, modifying a public enum can break client assemblies. Consider the following example:

// E.cs

public enum E {A, B, C}

// P.cs

using System;

class Program {
  static void Main () {
    Console.WriteLine(E.A);
  }
}

Running the program P.exe outputs the string A. After changing E.cs to public enum E {B, C} the output of the same program is now B. The error remains hidden until P.cs is recompiled. Hence, we use a sealed class with a private constructor and static readonly (not const) values instead:

public sealed class E {
  E () {}
  public static readonly int A = 0, B = 1, C = 2;
}

Note that this approach is no less type-safe than an enum would be, as it is always possible to assign arbitrary values to an enum variable using a typecast. Even the self-documenting nature of enums can be preserved, with the following pattern:

public sealed class E {
  E () {}
  public static readonly E A = new E(), B = new E(), C = new E();
}

Such an approach is slightly less concise than an enum, but it eliminates the class of errors described above. Those still unconvinced might want to read the plethora of caveats, dos and don'ts regarding enum design at Krzysztof Cwalina's blog.