CodeGym /Cursos /C# SELF /Creación de eventos con delegates (

Creación de eventos con delegates ( event y delegate)

C# SELF
Nivel 52 , Lección 1
Disponible

1. Introducción

Entonces, cuando hablamos de «evento» (event) en el contexto de C#, nos referimos a un mecanismo que permite notificar de forma segura a uno o varios objetos que algo ocurrió. Los eventos no son magia: debajo son delegates (la palabra clave delegate) con protección adicional para evitar usos indebidos.

Imagina la suscripción a un canal de YouTube. El canal (el objeto-que-tiene-el-evento) tiene un botón "Suscribirse": cualquier espectador (otro objeto) puede pulsarlo y entrar en la lista de suscriptores (delegates-manejadores). Cuando el autor publica un nuevo vídeo (lanza el evento), solo los suscriptores reciben la notificación, y solo el autor del canal decide cuándo sucede. Detalle importante: los espectadores pueden suscribirse o darse de baja, pero no pueden enviar la notificación por sí mismos — ese derecho lo tiene solo el dueño del canal.

Un poco de sintaxis formal


// Declaración del delegate que definirá la "forma" del manejador del evento
public delegate void SimpleEventHandler();

// Declaración del evento basado en el delegate
public event SimpleEventHandler SomethingHappened;

Todo sencillo: event es la palabra clave para declarar un evento, y el tipo del evento siempre lo define un delegate.

2. Primer ejemplo simple: evento «¡Se pulsó el botón!»

Paso 1. Definimos el delegate para los manejadores del evento


// Manejador del evento: no devuelve nada, no acepta parámetros
public delegate void ButtonClickHandler();

Paso 2. Clase con el evento


public class Button
{
    // Evento: se puede suscribir y desuscribir, pero no se puede invocar desde fuera
    public event ButtonClickHandler Click;

    // Método que "pulsa el botón"
    public void Press()
    {
        Console.WriteLine("¡Botón pulsado!");
        // Llamada al evento: notificar a todos los suscriptores
        Click?.Invoke();
    }
}

Fíjate: el evento está declarado basado en un delegate, y en el método Press el evento se invoca con ?.Invoke(). ¿Por qué así? Porque el evento puede estar vacío (nadie se suscribió), entonces Click es null. El operador de llamada segura garantiza que los manejadores se ejecuten solo si hay suscriptores.

Paso 3. Suscribirse al evento y ejecutar el código


// Ejemplo de uso
public class Program
{
    public static void Main()
    {
        var button = new Button();

        // Añadimos un "oyente" (nos suscribimos al evento)
        button.Click += OnButtonClicked;
        button.Click += () => Console.WriteLine("¡Otro manejador más!");

        button.Press(); // Simulamos pulsar el botón

        // Se puede desuscribir del evento
        button.Click -= OnButtonClicked;
        button.Press();
    }

    // Manejador de evento normal
    public static void OnButtonClicked()
    {
        Console.WriteLine("Manejador: ¡Se pulsó el botón!");
    }
}

En la primera pulsación se ejecutarán ambos manejadores, en la segunda — solo la lambda.

3. Diferencias entre evento y delegate

A un delegate se le pueden asignar valores libremente e incluso reemplazar por completo la lista de suscriptores — alguien podría hacer algo como button.Click = null y todas las suscripciones anteriores «caerían».

Con un evento es distinto — está más ligado al objeto. Solo el dueño de la clase donde está declarado el evento puede invocarlo directamente (por ejemplo, Click() desde dentro de la clase). Cualquier otro código solo puede suscribirse con += o desuscribirse con -=, pero no puede "poner a null" todo de golpe. Esto protege la encapsulación y evita "romper" el sistema de suscripciones.

4. Firma: tipos de delegates para eventos, parámetros

La tradición en .NET es que un manejador de eventos reciba dos parámetros — object sender (quién lanzó el evento) y los argumentos del evento (EventArgs). Se recomienda usar los delegates EventHandler o EventHandler<TEventArgs>.

Ejemplo: delegate con parámetros


public delegate void ButtonClickHandler(object sender, EventArgs e);

EventArgs es la clase base para pasar información adicional. Si necesitas más datos, creas tu propia clase que herede de ella.

Apliquemos esto a nuestro botón


// Clase de argumentos del evento
public class ButtonClickEventArgs : EventArgs
{
    public string UserName { get; }

    public ButtonClickEventArgs(string userName)
    {
        UserName = userName;
    }
}

public class Button
{
    public event EventHandler<ButtonClickEventArgs> Click;

    public void Press(string userName)
    {
        Console.WriteLine("¡Botón pulsado!");
        Click?.Invoke(this, new ButtonClickEventArgs(userName));
    }
}

public static void Main()
{
    var button = new Button();
    button.Click += OnButtonClicked;

    button.Press("Vasilii");
}

public static void OnButtonClicked(object sender, ButtonClickEventArgs e)
{
    Console.WriteLine($"¡El usuario {e.UserName} pulsó el botón!");
}

5. Esquema visual: cómo funciona un evento


+-------------+
| Usuario     |
+-------------+
      |
      v
+--------------+
|  Button.Press|
+--------------+
      |
      v
+-----------------+         +------------------------+
|  Llamada Click? |----->---|   Suscriptor 1         |
+-----------------+         +------------------------+
      |                     |   Ejecutar manejador   |
      v                     +------------------------+
+-----------------+         +------------------------+
|   Si hay       |----->---|   Suscriptor 2         |
|   suscriptor   |         +------------------------+
      :                     |   Ejecutar manejador   |
      v                     +------------------------+

Button.Press invoca el evento; los manejadores de los suscriptores se ejecutan en orden.

6. Matices útiles

Forma recomendada de eventos en .NET

Usa EventHandler y EventHandler<TEventArgs> para que tu código sea compatible con librerías y herramientas. Este enfoque facilita la evolución de los eventos: puedes añadir nuevas propiedades a tus EventArgs sin romper a los suscriptores.

Documentación: EventHandler, event.

Eventos vs delegates

Si necesitas ligar un componente a un método concreto — basta un delegate (delegate). Si en cambio quieres que diferentes partes del programa puedan suscribirse y desuscribirse en cualquier momento — usa un evento (event).

7. Errores comunes y momentos incómodos al crear eventos

Error nº1: invocar un evento sin comprobar null. Si no hay suscriptores, intentar invocarlo directamente provocará una NullReferenceException. Usa la llamada segura:


Click?.Invoke(...);

Error nº2: intentar invocar el evento desde fuera de la clase. Un evento solo puede ser levantado (invocado) dentro de la clase donde está declarado. Desde otra clase el compilador dará error — esto protege la encapsulación.

Error nº3: suscripción múltiple al mismo manejador. Si un mismo manejador se suscribe varias veces, se ejecutará tantas veces como se haya añadido. Es una característica del mecanismo de suscripción. Vigila las duplicaciones de += y asegura la desuscripción correcta con -=.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION