Módulo 08: Enumeraciones

Objetivos del módulo

  • Comprender qué es un enum y por qué usarlo
  • Crear y utilizar enumeraciones
  • Usar el atributo [Flags] para combinaciones
  • Convertir entre enums, enteros y strings

1. ¿Qué es una enumeración?

📘 Concepto: Un enum (enumeración) es un tipo de dato que contiene un conjunto fijo de constantes con nombre. Se usa para representar opciones cerradas: días de la semana, estados de un pedido, direcciones, etc.

El problema sin enums

// SIN enum: usamos números "mágicos"
int estadoPedido = 2;

if (estadoPedido == 0) Console.WriteLine("Pendiente");
else if (estadoPedido == 1) Console.WriteLine("Procesando");
else if (estadoPedido == 2) Console.WriteLine("Enviado");
else if (estadoPedido == 3) Console.WriteLine("Entregado");

// ¿Qué significa el 2? ¿Y si alguien pone 99? No hay control.

La solución con enums

enum EstadoPedido
{
    Pendiente,    // 0
    Procesando,   // 1
    Enviado,      // 2
    Entregado,    // 3
    Cancelado     // 4
}

EstadoPedido estado = EstadoPedido.Enviado;

if (estado == EstadoPedido.Enviado)
{
    Console.WriteLine("Tu pedido está en camino");
}

Ventajas:

  • Legible: EstadoPedido.Enviado es mucho más claro que 2
  • Seguro: el compilador no permite valores no definidos
  • Autocompletado: VS Code te muestra todas las opciones

2. Crear y usar enums

Declaración

// Los enums se declaran FUERA de las funciones (al principio del archivo)
enum DiaSemana
{
    Lunes,      // 0
    Martes,     // 1
    Miércoles,  // 2
    Jueves,     // 3
    Viernes,    // 4
    Sábado,     // 5
    Domingo     // 6
}

enum Prioridad
{
    Baja = 1,
    Media = 5,
    Alta = 10,
    Urgente = 100
}

📘 Concepto: Por defecto, los valores empiezan en 0 y se incrementan de uno en uno. Puedes asignar valores personalizados si lo necesitas.

Uso básico

DiaSemana hoy = DiaSemana.Miércoles;
Console.WriteLine(hoy);         // Miércoles
Console.WriteLine((int)hoy);   // 2

Prioridad p = Prioridad.Alta;
Console.WriteLine(p);           // Alta
Console.WriteLine((int)p);     // 10

Enums con switch

Los enums y switch son compañeros ideales:

enum Semaforo { Rojo, Amarillo, Verde }

Semaforo luz = Semaforo.Verde;

switch (luz)
{
    case Semaforo.Rojo:
        Console.WriteLine("¡PARA!");
        break;
    case Semaforo.Amarillo:
        Console.WriteLine("Precaución, va a cambiar");
        break;
    case Semaforo.Verde:
        Console.WriteLine("Puedes avanzar");
        break;
}

// Switch expression (moderno)
string accion = luz switch
{
    Semaforo.Rojo => "Parar",
    Semaforo.Amarillo => "Precaución",
    Semaforo.Verde => "Avanzar",
    _ => "Desconocido"
};

3. Conversiones

Entre enum e int

enum Color { Rojo, Verde, Azul }

// enum → int
Color c = Color.Verde;
int numero = (int)c;          // 1

// int → enum
Color c2 = (Color)2;         // Color.Azul
Console.WriteLine(c2);        // Azul

Entre enum y string

// enum → string
Color c = Color.Rojo;
string nombre = c.ToString();  // "Rojo"

// string → enum
Color c2 = Enum.Parse<Color>("Verde");
Console.WriteLine(c2);  // Verde

// string → enum (seguro, sin excepción)
if (Enum.TryParse<Color>("Azul", out Color resultado))
{
    Console.WriteLine($"Convertido: {resultado}");
}
else
{
    Console.WriteLine("Valor no válido");
}

// Ignorar mayúsculas/minúsculas
Enum.TryParse<Color>("rojo", ignoreCase: true, out Color r);  // Funciona

4. Métodos útiles de Enum

enum Estacion { Primavera, Verano, Otoño, Invierno }

// Obtener todos los valores
Estacion[] valores = Enum.GetValues<Estacion>();
foreach (Estacion e in valores)
{
    Console.WriteLine($"{e} = {(int)e}");
}
// Primavera = 0
// Verano = 1
// Otoño = 2
// Invierno = 3

// Obtener todos los nombres como string
string[] nombres = Enum.GetNames<Estacion>();
Console.WriteLine(string.Join(", ", nombres));
// Primavera, Verano, Otoño, Invierno

// Comprobar si un valor existe
Console.WriteLine(Enum.IsDefined<Estacion>((Estacion)2));  // True (Otoño)
Console.WriteLine(Enum.IsDefined<Estacion>((Estacion)99)); // False

5. Enums con [Flags]: combinación de valores

📘 Concepto: El atributo [Flags] permite que un enum represente combinaciones de valores, usando operadores de bits. Cada valor debe ser una potencia de 2.

[Flags]
enum Permisos
{
    Ninguno   = 0,     // 0000
    Leer      = 1,     // 0001
    Escribir  = 2,     // 0010
    Ejecutar  = 4,     // 0100
    Eliminar  = 8,     // 1000
    // Combinación predefinida
    Todos = Leer | Escribir | Ejecutar | Eliminar  // 1111 = 15
}

// Combinar permisos con |
Permisos usuario = Permisos.Leer | Permisos.Escribir;
Console.WriteLine(usuario);  // "Leer, Escribir"

// Comprobar si tiene un permiso con HasFlag
if (usuario.HasFlag(Permisos.Leer))
{
    Console.WriteLine("Puede leer");
}

if (!usuario.HasFlag(Permisos.Ejecutar))
{
    Console.WriteLine("No puede ejecutar");
}

// Añadir un permiso
usuario |= Permisos.Ejecutar;  // Ahora tiene Leer, Escribir, Ejecutar

// Quitar un permiso
usuario &= ~Permisos.Escribir;  // Ahora tiene Leer, Ejecutar
Console.WriteLine(usuario);     // "Leer, Ejecutar"

6. Ejemplo práctico: Juego de rol

enum ClasePersonaje { Guerrero, Mago, Arquero, Sanador, Pícaro }
enum Raza { Humano, Elfo, Enano, Orco }

static void MostrarPersonaje(string nombre, ClasePersonaje clase, Raza raza)
{
    Console.WriteLine($"╔══════════════════════════╗");
    Console.WriteLine($"  Nombre: {nombre}");
    Console.WriteLine($"  Clase:  {clase}");
    Console.WriteLine($"  Raza:   {raza}");
    Console.WriteLine($"  Stats base:");

    var (ataque, defensa, magia) = clase switch
    {
        ClasePersonaje.Guerrero => (15, 12, 3),
        ClasePersonaje.Mago     => (5, 6, 18),
        ClasePersonaje.Arquero  => (12, 8, 5),
        ClasePersonaje.Sanador  => (4, 7, 16),
        ClasePersonaje.Pícaro   => (10, 6, 8),
        _ => (8, 8, 8)
    };

    int bonoAtaque = raza == Raza.Orco ? 3 : 0;
    int bonoMagia = raza == Raza.Elfo ? 3 : 0;
    int bonoDefensa = raza == Raza.Enano ? 3 : 0;

    Console.WriteLine($"    Ataque:  {ataque + bonoAtaque}");
    Console.WriteLine($"    Defensa: {defensa + bonoDefensa}");
    Console.WriteLine($"    Magia:   {magia + bonoMagia}");
    Console.WriteLine($"╚══════════════════════════╝");
}

MostrarPersonaje("Aragorn", ClasePersonaje.Guerrero, Raza.Humano);
MostrarPersonaje("Legolas", ClasePersonaje.Arquero, Raza.Elfo);

7. Ejercicios

Ejercicio 1: Menú de restaurante

Crea enums para categorías de platos (Entrante, Principal, Postre, Bebida). El programa muestra un menú, permite elegir categoría y plato, y calcula el total del pedido.

Ejercicio 2: Máquina de estados

Simula un cajero automático con estados: Apagado, Esperando, Autenticando, MenuPrincipal, Retirando, Error. Controla las transiciones válidas entre estados.

Ejercicio 3: Permisos con Flags

Crea un sistema de permisos para una app con [Flags]: Leer, Crear, Editar, Eliminar, Admin. Define roles predefinidos (Visitante=Leer, Editor=Leer Crear Editar, Admin=Todos). Permite asignar y comprobar permisos.

Ejercicio 4: Agenda semanal

Crea una agenda que use el enum DiaSemana. El usuario puede añadir tareas a un día, ver las tareas de un día, y listar todos los días con sus tareas.


Resumen

Concepto Ejemplo
Declarar enum enum Color { Rojo, Verde, Azul }
Usar Color c = Color.Rojo;
Enum → int int n = (int)c;
Int → enum Color c = (Color)0;
Enum → string string s = c.ToString();
String → enum Enum.Parse<Color>("Rojo")
String → enum (seguro) Enum.TryParse<Color>("Rojo", out var c)
Todos los valores Enum.GetValues<Color>()
Flags (combinar) permisos = Perm.A \| Perm.B
Flags (comprobar) permisos.HasFlag(Perm.A)