1. Sintaxis de suscripción a un evento
Imagina una aplicación para gestionar tareas: cuando una tarea se completa, se dispara un evento y un handler puede, por ejemplo, enviar una notificación, actualizar la UI o escribir info en el log. C# hace que suscribirse a eventos sea cómodo y seguro: indicas explícitamente qué handler reacciona a qué evento.
Suscribirse a un evento en C# es casi como apuntarte en la lista de invitados de una fiesta:
publisher.MyEvent += HandlerMethod;
Aquí publisher es el objeto que declaró el evento MyEvent, y HandlerMethod es el método que se llamará cuando ocurra ese evento.
Veamos esto en el contexto de un ejemplo mínimo. Supongamos que tenemos una app para contar clicks:
public class Clicker
{
public event Action Clicked;
public void Click()
{
// ¡Algo se ha hecho click!
Clicked?.Invoke();
}
}
Este es nuestro "publisher" del evento Clicked. Ahora — vamos a suscribir un handler:
Clicker clicker = new Clicker();
void OnClicked()
{
Console.WriteLine("¡El botón fue pulsado!");
}
clicker.Clicked += OnClicked;
// En alguna parte del código
clicker.Click();
// → "¡El botón fue pulsado!"
¿Cómo funciona esto? Al evento de "click" añadimos nuestro método OnClicked, y se invocará cada vez que ocurra un click.
2. Handlers de eventos: cuáles existen y cómo declararlos
Un handler de evento es un método que se ejecuta cuando se dispara el evento. Su firma debe coincidir con el tipo de delegate del evento. Por ejemplo, si tenemos el evento declarado como public event Action Clicked;, entonces el handler debe ser un método sin parámetros y sin valor de retorno.
Handler para Action
void OnClicked()
{
Console.WriteLine("¡Ocurrió el evento (Action)!");
}
Handler para el estándar EventHandler
Cuando usas el enfoque clásico con EventHandler, el handler acepta dos parámetros: el sender (object sender) y los datos del evento (EventArgs e):
public class Alarm
{
public event EventHandler AlarmRaised;
public void RaiseAlarm()
{
AlarmRaised?.Invoke(this, EventArgs.Empty);
}
}
Alarm alarm = new Alarm();
void AlarmHandler(object sender, EventArgs e)
{
Console.WriteLine("¡Se activó la alarma!");
}
alarm.AlarmRaised += AlarmHandler;
alarm.RaiseAlarm();
Métodos anónimos y expresiones lambda
C# permite usar funciones anónimas y expresiones lambda directamente al suscribirse:
clicker.Clicked += () => Console.WriteLine("¡Otro click!");
O algo un poco más complejo, si el evento tiene argumentos:
alarm.AlarmRaised += (sender, e) =>
{
Console.WriteLine($"Alarm raised by: {sender}");
};
4. Matices útiles
Suscripción y desuscripción: matices importantes
La vida real es cuando hay demasiados invitados en la fiesta, o alguien quiere irse a casa. Con los delegates pasa lo mismo: un handler se puede añadir (suscribir) y quitar (desuscribir):
// Suscripción
publisher.MyEvent += MyHandler;
// Desuscripción (cuando el handler ya no es necesario)
publisher.MyEvent -= MyHandler;
¿Por qué es importante? Si no te desuscribes, especialmente en apps grandes, los handlers pueden quedarse "colgando" y evitar que el garbage collector libere objetos — de ahí vienen las fugas de memoria.
Varios handlers
Puedes suscribir tantos handlers como quieras a un mismo evento. Se ejecutarán en orden de suscripción.
clicker.Clicked += () => Console.WriteLine("¡Primer handler!");
clicker.Clicked += () => Console.WriteLine("¡Segundo handler!");
clicker.Click();
// → ¡Primer handler!
// → ¡Segundo handler!
Incluso puedes suscribir y desuscribir handlers "sobre la marcha" — es flexible y cómodo.
Por qué esto es realmente importante: escenarios reales
- UI (Windows Forms, WPF, WinUI, MAUI): click en botón, cambio de texto en un campo — todo eso son eventos.
- FileSystemWatcher: notificación de nuevos archivos en una carpeta.
- Operaciones asíncronas: finalización de descarga de un archivo, progreso de tareas.
- Sistema de plugins: módulos separados se suscriben a eventos de la app principal.
El modelo basado en eventos permite construir arquitecturas extensibles: puedes añadir módulos nuevos y suscribirte a eventos existentes sin cambiar el código base.
Ejemplos de suscripción
| Firma del evento | Ejemplo de suscripción | Ejemplo de handler |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
5. Errores típicos y peligros
Error №1: desajuste de firmas del handler.
Si el evento se declara como event Action<int>, y tratas de suscribir un método sin parámetros, el compilador dará error. Siempre comprueba que el método coincide con la firma requerida por el evento.
Error №2: captura de variables en lambdas.
Al suscribir con una lambda, esta puede capturar variables del scope exterior (ver tema "Closures"). Si la variable cambia después de la suscripción, el handler verá el nuevo valor, lo que puede producir resultados inesperados.
Error №3: suscribirse a un evento de un objeto no inicializado.
Si te suscribes a un evento antes de que exista o esté inicializado el objeto que lo contiene, corres el riesgo de obtener un NullReferenceException. Antes de suscribirte asegúrate de que el objeto está listo para usarse.
Error №4: suscripción múltiple del mismo handler.
Si el mismo handler está suscrito varias veces a un evento, se invocará tantas veces como suscripciones haya. No es un bug, es la forma en que funcionan los eventos, pero a menudo resulta una sorpresa desagradable.
GO TO FULL VERSION