Delegados y eventos
Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy — lección 43 de 70.
Un delegado es un tipo que representa una referencia a un método. Piénselo como una variable que contiene un método en lugar de datos. Esto le permite pasar métodos como argumentos, almacenarlos en colecciones y llamarlos dinámicamente.
Se declara un delegado especificando la firma del método al que puede hacer referencia:
public delegate void Notify(string message);
public class Publisher
{
public void SendAlert(string msg) => Console.WriteLine("Alert: " + msg);
}
// Uso
Notify notifier = new Publisher().SendAlert;
notifier("System started"); // Alerta: System startedLos eventos se basan en los delegados para implementar el patrón de publicación-suscripción. Una clase lanza un evento y otras clases pueden suscribirse para recibir notificaciones. La palabra clave event restringe cómo se puede utilizar el delegado: los suscriptores solo pueden agregar o eliminar controladores, no invocar el evento directamente.
public class Button
{
public event Action Clicked;
public void Click()
{
Clicked?.Invoke(); // Notificar a todos los suscriptores
}
}
// Suscripción
Button btn = new Button();
btn.Clicked += () => Console.WriteLine("Button was clicked!");
btn.Click(); // ¡Se hizo clic en el botón!El operador += suscribe un controlador, mientras que -= cancela la suscripción. El patrón ?.Invoke() llama al evento de forma segura solo si hay suscriptores. Los eventos son fundamentales para las aplicaciones GUI, los motores de juegos y cualquier sistema donde los objetos necesiten reaccionar a los cambios sin un acoplamiento fuerte.
Desafío
FácilVamos a construir un sistema de monitoreo de temperatura que utiliza delegados y eventos para notificar a los suscriptores cada vez que la temperatura cambia. Este es un ejemplo clásico del patrón de publicación-suscripción que los eventos habilitan de manera tan elegante.
Organizarás tu código en dos archivos:
TemperatureMonitor.cs: Crea una claseTemperatureMonitoren el espacio de nombresMonitoringque actúe como el publicador. Tu monitor debe:- Tener un campo privado para almacenar la temperatura actual
- Declarar un evento usando
Action<int>llamadoTemperatureChangedque se active cada vez que se actualice la temperatura - Tener una propiedad
Temperature(int) donde el setter actualice el campo privado y active el evento con el nuevo valor de temperatura
?.Invoke()para activar el evento de forma segura solo cuando haya suscriptores.Program.cs: En tu archivo principal, crea unTemperatureMonitory suscribe dos controladores diferentes a su eventoTemperatureChanged:- Un controlador que imprima:
Alert: Temperature is now {value} degrees - Otro controlador que imprima:
Logged: {value}
- Un controlador que imprima:
Recibirás dos entradas:
- Primer valor de temperatura (entero)
- Segundo valor de temperatura (entero)
Por ejemplo, si las entradas son 25 y 30, la salida debería ser:
Alert: Temperature is now 25 degrees
Logged: 25
Alert: Temperature is now 30 degrees
Logged: 30Observa cómo establecer la propiedad Temperature notifica automáticamente a todos los suscriptores; el monitor no necesita saber nada sobre quién está escuchando. ¡Esa es la belleza del enfoque orientado a eventos!
Hoja de referencia
Un delegado es un tipo que representa una referencia a un método, lo que permite pasar métodos como argumentos, almacenarlos en colecciones y llamarlos dinámicamente.
Declare un delegado especificando la firma del método:
public delegate void Notify(string message);
public class Publisher
{
public void SendAlert(string msg) => Console.WriteLine("Alert: " + msg);
}
// Uso
Notify notifier = new Publisher().SendAlert;
notifier("System started"); // Alert: System startedLos eventos implementan el patrón de publicación-suscripción utilizando delegados. La palabra clave event restringe el uso del delegado: los suscriptores solo pueden agregar o eliminar controladores, no invocar el evento directamente.
public class Button
{
public event Action Clicked;
public void Click()
{
Clicked?.Invoke(); // Notificar a todos los suscriptores
}
}
// Suscribiéndose
Button btn = new Button();
btn.Clicked += () => Console.WriteLine("Button was clicked!");
btn.Click(); // Button was clicked!Use += para suscribir un controlador y -= para cancelar la suscripción. El patrón ?.Invoke() llama al evento de forma segura solo si hay suscriptores.
Pruébalo tú mismo
using System;
using Monitoring;
class Program
{
public static void Main(string[] args)
{
// Leer entrada
int temp1 = Convert.ToInt32(Console.ReadLine());
int temp2 = Convert.ToInt32(Console.ReadLine());
// TODO: Crear una instancia de TemperatureMonitor
// TODO: Suscribir un manejador que imprima: "Alert: Temperature is now {value} degrees"
// TODO: Suscribir otro manejador que imprima: "Logged: {value}"
// TODO: Establecer la temperatura a temp1
// TODO: Establecer la temperatura a temp2
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosNamespaces y directivasIntro a clases y objetosLa palabra clave 'this'Métodos y parámetrosCampos vs PropiedadesConstructoresInicializadores de objetosResumen - Calculadora simple4Herencia
Sintaxis básica de herencia (:)La palabra clave 'base'Palabras clave Virtual y OverrideClases selladas (Sealed)La clase base 'object'Resumen: Jerarquía de empleados7Características avanzadas
Sobrecarga de operadoresIndexadores (this[])Sobrescritura de ToString()Métodos de extensiónResumen - Lista personalizada10Patrones de diseño - Parte 1
Introducción a los patrones de diseñoSingleton Thread-SafePatrón FactoryPatrón Observer (Eventos)Patrón Strategy2Propiedades y miembros estáticos
Propiedades autoimplementadasPropiedades de solo lectura y escrituraCampos y métodos estáticosClases estáticasMiembros con cuerpo de expresión5Polimorfismo e Interfaces
Polimorfismo: Compilación vs. EjecuciónInterfaz vs. Clase AbstractaInterfaces MúltiplesInterfaces ExplícitasUpcasting y DowncastingRepaso: Calculadora de Figuras8Conceptos avanzados de POO
Composición sobre herenciaGenéricos (clases y métodos)Delegados y eventosAtributos y ReflexiónIDisposable y la sentencia usingFundamentos de Inyección de dependencias11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Arquitectura de clases
Datos de instancia vs. estáticosPalabras clave 'readonly' y 'const'Campos de respaldoResumen - Gestor de cuentas bancarias6Encapsulamiento
Modificadores de accesoPropiedades para el encapsulamientoImplementación de ocultamiento de datosPatrones de inmutabilidadRepaso - Registros de estudiantes