🎮 UnityDocs

Delegados, Action y Func a fondo

Qué es un delegado (una variable que guarda una función), cómo usar Action y Func, las expresiones lambda y los callbacks. La base sobre la que se construyen los eventos y muchos patrones limpios.

📄 ArtículoAvanzado✅ RevisadoUnity 6.0 LTSActualizado 2026-07-15
DificultadConceptual Código Matemáticas
Tiempo📖 Lectura 8 min · ⌨️ Práctica 20 min

La idea: una variable que guarda una función#

Un delegado es una variable que, en vez de guardar un número o un texto, guarda una función. Eso te permite pasar comportamiento de un lado a otro: almacenarlo, pasarlo como parámetro y ejecutarlo más tarde. Es la base de los eventos, de LINQ y de muchos patrones limpios.

Action y Func: los delegados listos para usar#

Casi nunca necesitas declarar un delegado a mano: C# trae dos genéricos que cubren todo.

C#
using System;

// Action: NO devuelve nada. El <...> son los parámetros que recibe.
Action saludar = () => Debug.Log("Hola");
Action<int> mostrarDaño = (n) => Debug.Log("Daño: " + n);
Action<string, int> log = (msg, code) => Debug.Log(msg + " " + code);

// Func: SÍ devuelve. El ÚLTIMO tipo del <...> es lo que devuelve.
Func<int> dado = () => UnityEngine.Random.Range(1, 7);
Func<int, int, int> sumar = (a, b) => a + b;

// Ejecutarlos:
saludar();                 // Hola
mostrarDaño(10);           // Daño: 10
int total = sumar(2, 3);   // 5
Cómo leer Action vs Func

Action<int, string> → recibe un int y un string, no devuelve nada. Func<int, string, bool> → recibe un int y un string, y devuelve un bool (el último tipo siempre es el retorno). Regla: Action = hacer (void), Func = calcular (devuelve algo).

Lambdas: funciones sin nombre#

Ese (a, b) => a + b es una expresión lambda: una función anónima, escrita en el sitio. => se lee "va a". Son comodísimas para funciones pequeñas que solo usas una vez:

C#
// En vez de declarar un método aparte:
enemigos.Where(e => e.Vida > 0);       // lambda como filtro (LINQ)
boton.onClick.AddListener(() => Debug.Log("clic"));
Invoke(() => Reaparecer(), 2f);

También puedes pasar un método con nombre donde se espera un delegado (sin paréntesis, sin ejecutarlo):

C#
boton.onClick.AddListener(Jugar);   // pasa el método Jugar, no lo llama
void Jugar() => Debug.Log("¡A jugar!");

Callbacks: "avísame cuando termines"#

Un uso clásico: pasar una función que se ejecutará cuando algo acabe. Es el patrón callback.

C#
public void CargarNivel(string nombre, Action alTerminar)
{
    // ...carga...
    alTerminar?.Invoke();   // avisa a quien lo pidió
}

// Uso:
CargarNivel("Nivel1", () => Debug.Log("Nivel cargado, arranca la música"));
El operador ?.Invoke()

alTerminar?.Invoke() significa "si hay una función asignada, ejecútala; si es null, no hagas nada". Sin el ?., invocar un delegado nulo lanza NullReferenceException. Es el mismo patrón que en los eventos.

Multicast: varias funciones en un delegado#

Un delegado puede guardar varias funciones a la vez con +=. Al invocarlo, se ejecutan todas. Esto es justo lo que hace posible que muchos escuchen un evento:

C#
Action alMorir = null;
alMorir += MostrarGameOver;
alMorir += GuardarPuntuacion;
alMorir += ReproducirSonido;
alMorir?.Invoke();   // ejecuta las tres

Delegados vs eventos#

Un event (ver eventos) es un delegado con protección: desde fuera solo puedes suscribirte (+=) o darte de baja (-=), no invocarlo ni borrar los suscriptores de otros. Para comunicación entre objetos, usa event; para pasar una función puntual (un callback, un filtro), un Action/Func normal.

Errores frecuentes#

  • Invocar un delegado nulo sin ?. (excepción).
  • Confundir pasar el método (Jugar) con llamarlo (Jugar()) al asignarlo a un delegado.
  • Suscribir lambdas a eventos y no poder darse de baja (una lambda anónima no se puede quitar con -=; usa un método con nombre si necesitas desuscribir).
  • Liarse con Action/Func: recuerda, Func devuelve (último tipo), Action no.

Ponte a prueba#

📝 Pon a prueba lo aprendido

Resumen#

Un delegado es una variable que guarda una función. Usa Action (no devuelve) y Func (devuelve, último tipo) en vez de declararlos a mano, y lambdas (x => ...) para funciones pequeñas en línea. Sirven para callbacks ("avísame al terminar") y, en versión multicast (+=), sostienen los eventos. Invoca siempre con ?.Invoke() y recuerda: las lambdas anónimas no se pueden desuscribir.

Fuentes y para profundizar