Please, don't use enums in C#

Enumerated types

Enumerated (discrete) types are a powerful modeling tool for software developers: they allows them to explicitly state all and only the permitted values a variable can hold, with guarantee that

• no invalid values can be pushed into a function, and
• conditional (switch/case or pattern-matching based) depending on enumerated types can be recognized to be exhaustive by compilers.

This is strictly true for enums you can define in languages like Scala (case classes), Kotlin (enums) or even the old, mistreated Java (enums), but is only an unmaintained, misleading promise for C#’s enums.

The problem (or “The C# way” to enums”)

Defining an enum in C# indeed is only a syntactic sugar you can leverage to define related, “namespaced” integer constants:

is in essence only a shortcut for

I’m not saying the compiler produces the same output - I’m saying in both cases you can refer to something like Ordinal.Second in order to get an int constant whose value is 2.

Issue #1

No way to define a method, say

preventing callers to pass invalid values into:

is definitely valid code (from the compiler’s point of view) producing the following output:

Issue #2

No way to rely on compiler in order to check exhaustiveness of conditional checks: you can indeed write

but the compilers gives you a warning like The switch expression does not handle all possible inputs (is is not exhaustive), even if all values defined by the enum are explicitly treated; in order to avoid this inappropriate warning you must add a fourth, never used branch to the switch:

(or you can return a special value, if you like code smells ;-)…).

So, C#’s enums are syntactic sugar for int constants, and defining a method parameter of type Ordinal is nothing different from defining it of type int (yes, you can define an enum having byte or long or \${other integral type} as underlying representation (see here), but… you got the idea).

From a modelling point of view, C#’s enums are a very poor feature, which does not allow developers to define true enumerated (discrete) types: so… please, don’t use them, or at least don’t use them as if they were.

The right way

The right way to model enumerated/discrete types in C# is imho to adopt a pattern that I first heard about in 2004, reading Hardcore Java enlightening book:

This does not solve the problem of exhaustiveness’ check, but models true discrete type allowing only intended values to be used where Ordinal parameters are required.

A variant of this pattern, based on inheritance, allow developers to attach polymorphic behaviours to enumeration cases.

Bonus (or “The Java way to enums”)

By the way, this is the way enum‘s implementation in Java (since 2004!!!) and Kotlin work: they provide substantially a syntactic sugar for the pattern above, allowing true enumerated/discrete types modelling:

Java supports exhaustiveness check out of the box, too: