Enumerations, or enums, are a special data type that allows programmers to define a set of named constants under a single type.
The main advantage of using enums is that they provide readability and structure to the code, as they replace “magic” numbers or strings with meaningful, prefixed, established names.
“Magic” numbers and strings are literals that someone adds to the code without explaining to the next person very well where they come from. For example:
var nota = suma * 1.271; // where does 1.271 come from? who knows
The most typical (and somewhat boring) example usually given is the days of the week.
enum DiaSemana {
Lunes, Martes, Miercoles, Jueves, Viernes
};
DiaSemana hoy = DiaSemana.Miercoles;
This helps with code maintainability. For example, if we need to add or modify one of the constants, we only have to change the enum. In our case, if you want to add sabado and domingo, you only need to modify it in one place.
Finally, they prevent errors by restricting the values we can use to those defined in the enum, avoiding, for example, someone misspelling a value by mistake. For instance, you protect against someone mistakenly writing juevs (missing an ‘e’).
Furthermore, in typed languages, it performs type checking for us and prevents us from mixing them by mistake. For example, putting a Mes value into a variable of type DiaSemana.
In some languages, enumerations can only be associated with integer numbers, while other languages allow other values like strings.
Examples of Enums in Different Languages
Let’s see how to create an ENUM in different languages.
For example, suppose we have the status of a task. The possible statuses are not started, pending, in progress, and completed.
In C# we have the concept of ENUM natively through the reserved word enum.
// Definition of an enumeration in C#
public enum Estado {
SinComenzar,
Pendiente,
EnProgreso,
Finalizado
};
// Assigning the initial status to the estadoActual variable
Estado estadoActual = Estado.SinComenzar;
In C++ it also has the concept of ENUM using the reserved word enum.
// Definition of an enumeration in C++
enum Estado {
SinComenzar,
Pendiente,
EnProgreso,
Finalizado
};
// Assigning the initial status to the estadoActual variable
Estado estadoActual = Estado::SinComenzar;
In JavaScript, we can use immutable objects to simulate enumerations. In the following code snippet, we define an immutable object called Estado using Object.freeze().
// Definition of an immutable object in JavaScript using Object.freeze()
const Estado = Object.freeze({
SinComenzar: "sin_comenzar",
Pendiente: "pendiente",
EnProgreso: "en_progreso",
Finalizado: "finalizado"
});
// Assigning the initial status to the estadoActual variable
let estadoActual = Estado.SinComenzar;
In Python, starting from version 3.4, we can use the Enum class from the enum module to define enumerations.
# Definition of an enumeration in Python using the Enum class
from enum import Enum
class Estado(Enum):
SinComenzar = "sin_comenzar"
Pendiente = "pendiente"
EnProgreso = "en_progreso"
Finalizado = "finalizado"
# Assigning the initial status to the estadoActual variable
estadoActual = Estado.SinComenzar
Best Practices Tips
Enumerations are a very useful tool that allow us to represent a fixed set of discrete values in a clear and readable way. In general, it’s a good idea to use them.
However, in some cases, their use can lead to over-engineering, especially when it comes to their integration with other parts of the system.
For example, consider an Estado enumeration that represents the different statuses of a process. If we want to save these statuses in a database using codes like:
| Estado | Value |
|---|---|
| SinComenzar | ”S” |
| Pendiente | ”P” |
| EnProgreso | ”E” |
| Finalizado | ”F” |
To do this, we would need to perform mappings between the enumeration values and the codes when reading from and writing to the database.
// Function to map from string to enumeration
public static Estado MapearStringAEnum(string estadoString) {
switch (estadoString) {
case "S":
return Estado.SinComenzar;
case "P":
return Estado.Pendiente;
case "E":
return Estado.EnProgreso;
case "F":
return Estado.Finalizado;
default:
throw new ArgumentException("Invalid state");
}
}
// Function to map from enumeration to string
public static string MapearEnumAString(Estado estadoEnum) {
switch (estadoEnum) {
case Estado.SinComenzar:
return "S";
case Estado.Pendiente:
return "P";
case Estado.EnProgreso:
return "E";
case Estado.Finalizado:
return "F";
default:
throw new ArgumentException("Invalid state");
}
}
This process can unnecessarily complicate our code and increase its complexity. A simpler alternative in this case would be to use simple constants instead of an enumeration. For example:
// Definition of constants for the states
public const string SIN_COMENZAR = "S";
public const string PENDIENTE = "P";
public const string EN_PROGRESO = "E";
public const string FINALIZADO = "F";
// Assigning the initial status to the estadoActual variable
string estadoActual = SIN_COMENZAR;
It’s simpler, but we have just lost the semantics and (especially) the safety that enumerations provide us. For example, any string could be assigned to the estadoActual variable, even if it doesn’t represent a valid status.
To address this issue, there are alternative patterns, such as strongly typed enums, which provide the safety of stricter data types while preserving code readability and clarity.
In short, using Enums is good, and in general it’s advisable. But as always, use your head. Using them indiscriminately can complicate the code more than it helps us.
