Delegados y Eventos
Los delegados y eventos en C# son herramientas fundamentales para construir aplicaciones flexibles, modulares y desacopladas. Un delegado es un tipo que actúa como un puntero seguro a un método, permitiendo almacenar y llamar a métodos de manera dinámica en tiempo de ejecución. Los eventos se basan en delegados y permiten implementar el patrón publicador-suscriptor (Publisher-Subscriber), de manera que los objetos puedan reaccionar ante acciones de otros objetos sin acoplarse directamente a ellos.
En el desarrollo de software, los delegados y eventos se usan comúnmente en sistemas de notificaciones, interfaces de usuario, registro de logs, frameworks de extensibilidad y procesamiento asincrónico. Los delegados permiten invocar métodos sin conocer los detalles de implementación, mientras que los eventos permiten que múltiples suscriptores respondan a una acción compartida.
En este tutorial, aprenderás a definir, suscribirte y disparar eventos, a trabajar con delegados tipo seguro y a combinar estos conceptos con principios de programación orientada a objetos, estructuras de datos y algoritmos para resolver problemas reales en proyectos C#. También se mostrará cómo estos conceptos se integran dentro de la arquitectura del software para crear componentes desacoplados y mantenibles.
Ejemplo Básico
textusing System;
namespace DelegatesAndEventsDemo
{
public delegate void NotificationHandler(string message);
public class Publisher
{
public event NotificationHandler Notify;
public void SendNotification(string message)
{
Notify?.Invoke(message);
}
}
public class Subscriber
{
private string _name;
public Subscriber(string name)
{
_name = name;
}
public void OnNotificationReceived(string message)
{
Console.WriteLine($"{_name} recibió el mensaje: {message}");
}
}
class Program
{
static void Main(string[] args)
{
Publisher publisher = new Publisher();
Subscriber alice = new Subscriber("Alicia");
Subscriber bob = new Subscriber("Bob");
publisher.Notify += alice.OnNotificationReceived;
publisher.Notify += bob.OnNotificationReceived;
publisher.SendNotification("¡Hola suscriptores!");
publisher.Notify -= bob.OnNotificationReceived;
publisher.SendNotification("Segundo mensaje");
Console.ReadLine();
}
}
En este ejemplo, el delegado NotificationHandler
define la firma de los métodos que pueden asociarse al evento y garantiza seguridad de tipos. La clase Publisher
contiene el evento Notify
y lo invoca de forma segura usando ?.Invoke()
para evitar errores si no hay suscriptores.
La clase Subscriber
implementa el método OnNotificationReceived
compatible con la firma del delegado. En Main
, se crean instancias de Publisher y Subscriber, se suscriben y se cancelan suscripciones al evento, mostrando un patrón de observador y reduciendo acoplamientos. Esto previene problemas de memoria y facilita la mantenibilidad en aplicaciones reales.
Ejemplo Práctico
textusing System;
using System.Collections.Generic;
namespace DelegatesAndEventsAdvanced
{
public delegate void DataProcessedHandler(int result);
public class DataProcessor
{
public event DataProcessedHandler DataProcessed;
public void ProcessData(List<int> data)
{
int sum = 0;
foreach (var num in data)
{
if (num < 0)
{
Console.WriteLine("Se ignoró valor negativo: " + num);
continue;
}
sum += num;
}
DataProcessed?.Invoke(sum);
}
}
public class Logger
{
public void LogResult(int result)
{
Console.WriteLine($"Resultado registrado: {result}");
}
}
public class Notifier
{
public void SendAlert(int result)
{
if (result > 50)
Console.WriteLine("¡Alerta! Resultado por encima del límite: " + result);
}
}
class Program
{
static void Main(string[] args)
{
DataProcessor processor = new DataProcessor();
Logger logger = new Logger();
Notifier notifier = new Notifier();
processor.DataProcessed += logger.LogResult;
processor.DataProcessed += notifier.SendAlert;
List<int> sampleData = new List<int> { 10, 20, 30, -5 };
processor.ProcessData(sampleData);
Console.ReadLine();
}
}
En este ejemplo, DataProcessor
ignora valores negativos y suma los positivos. El evento DataProcessed
permite a Logger
y Notifier
reaccionar al resultado sin conocer detalles del procesamiento. Logger registra el resultado y Notifier genera alertas si se excede un umbral. Este ejemplo ilustra el patrón Observer, el principio de responsabilidad única (SRP), invocación segura de eventos, bajo acoplamiento y buenas prácticas en manejo de errores.
Mejores prácticas y errores comunes en C#:
- Usar delegados y eventos tipo seguro y encapsulados.
- Invocar eventos con
?.Invoke()
para evitar NullReferenceException. - Desuscribirse de eventos cuando ya no se necesiten para prevenir memory leaks.
- Evitar delegados anónimos de larga duración.
- No realizar operaciones largas en el hilo principal dentro de eventos frecuentes.
- Minimizar la asignación de objetos en eventos de alta frecuencia.
- Validar datos antes de disparar eventos.
📊 Tabla de Referencia
C# Element/Concept | Description | Usage Example |
---|---|---|
Delegate | Tipo seguro que apunta a un método | public delegate void MyDelegate(int x); |
Event | Evento basado en delegado | public event MyDelegate MyEvent; |
Subscription | Suscripción a un evento | myPublisher.MyEvent += mySubscriber.MyMethod; |
Unsubscription | Cancelar suscripción | myPublisher.MyEvent -= mySubscriber.MyMethod; |
Conditional Invocation | Invocación segura de evento | MyEvent?.Invoke(42); |
Anonymous Delegate | Delegado definido inline | myPublisher.MyEvent += (x) => Console.WriteLine(x); |
Resumen y próximos pasos:
Los delegados y eventos proporcionan una arquitectura flexible y desacoplada en aplicaciones C#. Conceptos clave incluyen delegados tipo seguro, definición de eventos, suscripción segura y el patrón Observer.
Para avanzar, se recomienda estudiar programación asincrónica, combinar delegados con LINQ y arquitecturas basadas en eventos. Aplicaciones prácticas incluyen sistemas de notificación, frameworks extensibles y procesamiento de datos en tiempo real. Recursos adicionales incluyen documentación oficial de Microsoft y proyectos open-source.
🧠 Pon a Prueba tu Conocimiento
Test Your Knowledge
Test your understanding of this topic with practical questions.
📝 Instrucciones
- Lee cada pregunta cuidadosamente
- Selecciona la mejor respuesta para cada pregunta
- Puedes repetir el quiz tantas veces como quieras
- Tu progreso se mostrará en la parte superior