Una de las aplicaciones más potentes y comunes de los delegados en C# es la implementación de callbacks. Esta técnica permite que un método ejecute una pieza de lógica externa una vez que ha finalizado su tarea principal, facilitando el desacoplamiento entre servicios.
Consideremos un escenario común: un sistema de registro de usuarios que, tras completar el proceso básico, debe disparar notificaciones por correo electrónico y mensajes de texto. Para evitar que el servicio de cuentas dependa directamente de los servicios de notificación, utilizamos un delegado Action.
En primer lugar, definimos el objeto de transferencia de datos (DTO) que contendrá la información del usuario:
public class UsuarioRegistroDto
{
public string NombreUsuario { get; set; }
public string Password { get; set; }
public string Correo { get; set; }
public string Telefono { get; set; }
}
A continuación, implementamos los servicios de notificación de forma indepandiente. Estos servicios no conocen la existencia del proceso de registro:
public class ServicioEmail
{
public void EnviarEmail(string direccion)
{
Console.WriteLine($"[Email] Notificación enviada a: {direccion}");
}
}
public class ServicioMensajeria
{
public void EnviarSms(string numero)
{
Console.WriteLine($"[SMS] Notificación enviada al número: {numero}");
}
}
La lógica central reside en el servicio de gestión de cuentas. Aquí es donde el delegado Action actúa como un puente para ejecutar código adicioanl sin necesidad de conocer los detalles de implementación de las notificaciones:
public class ProcesadorCuentas
{
public void EjecutarRegistro(UsuarioRegistroDto usuario, Action onCompletado = null)
{
// Lógica de persistencia o creación de cuenta
Console.WriteLine($"Creando cuenta para: {usuario.NombreUsuario}");
Console.WriteLine("Usuario almacenado en la base de datos.");
// Ejecución del callback si se ha proporcionado uno
onCompletado?.Invoke();
}
}
Finalmente, en la capa de orquestación (como un controlador o un servicio de aplicación), definimos qué acciones específicas deben ocurrir una vez que el registro sea exitoso:
public class RegistroController
{
private readonly ServicioEmail _emailSvc;
private readonly ServicioMensajeria _smsSvc;
private readonly ProcesadorCuentas _procesador;
public RegistroController(ServicioEmail emailSvc, ServicioMensajeria smsSvc, ProcesadorCuentas procesador)
{
_emailSvc = emailSvc;
_smsSvc = smsSvc;
_procesador = procesador;
}
public void RegistrarNuevoUsuario(UsuarioRegistroDto datos)
{
// Invocamos el servicio pasando una expresión lambda como callback
_procesador.EjecutarRegistro(datos, () =>
{
if (!string.IsNullOrEmpty(datos.Correo))
{
_emailSvc.EnviarEmail(datos.Correo);
}
if (!string.IsNullOrEmpty(datos.Telefono))
{
_smsSvc.EnviarSms(datos.Telefono);
}
});
}
}
Este enfoque permite que ProcesadorCuentas permanezca agnóstico respecto a cómo se notifica al usuario. Si en el futuro fuera necesario añadir una notificación vía WhatsApp o un sistema de logging, solo se requeriría modificar el callback en el controlador, manteniendo intacta la lógica de negocio del registro.