salon - ¿Cuándo usar devoluciones de llamadas en lugar de eventos en c#?
salones de eventos baratos (8)
Aunque las otras respuestas hasta ahora parecen razonables, tomaría una orientación más filosófica.
Una clase es un mecanismo que modela un tipo particular de cosa en un dominio particular. Al escribir los detalles internos de una clase, es muy fácil combinar los detalles de implementación del mecanismo con la semántica que se modela . Un breve ejemplo de lo que quiero decir:
class Giraffe : Mammal, IDisposable
{
public override void Eat(Food f) { ... }
public void Dispose() { ... }
}
Observe cómo hemos combinado lo que se está modelando en el mundo real (una jirafa es una especie de mamífero, una jirafa come comida) con los detalles de la implementación (una instancia de Jirafa es un objeto que se puede eliminar con el "uso "declaración". Te garantizo que si vas al zoológico, nunca verás una jirafa siendo eliminada con la declaración de uso. Hemos mezclado los niveles aquí, lo cual es desafortunado.
Intento usar eventos (y propiedades) como parte del modelo semántico y usar métodos de devolución de llamada (y campos) como parte del mecanismo . Haría de GaveBirth un evento de Giraffe, ya que es parte del modelo de comportamiento de jirafas del mundo real que estamos tratando de capturar. Si tuviera algún mecanismo, digamos que deseara implementar un algoritmo de recorrido transversal de árbol inorder-walk que caminase por el árbol genealógico de las jirafas y llamara un método en cada una, entonces diría que esto fue claramente un mecanismo y no parte del modelo, y hacer una devolución de llamada en lugar de tratar de calzar eso en el modelo de evento.
Perdóname si esta es una pregunta estúpida, admito que no lo he pensado mucho.
Pero, ¿cuándo preferirías usar una devolución de llamada (es decir, pasar un Func o una Acción), en lugar de exponer y usar un evento?
ACTUALIZAR
Lo que motivó esta pregunta fue el siguiente problema:
Tengo una clase ThingsHandler, que se puede asociar con un ThingEditor. The ThingsHandler maneja una lista de Cosas, conoce su orden, cuál es ''actual'', cuándo se agregan o eliminan nuevas, etc.
Los ThingEditors solo pueden modificar una sola cosa.
The ThingsHandler necesita alertar al ThingEditor cuando el usuario selecciona una nueva cosa para editar, y el ThingEditor necesita alertar al ThingsHandler cuando el usuario dice ''hecho''.
Lo que me molesta es que estas dos clases tengan referencias entre ellas, aunque supongo que es inevitable, o vinculadas a eventos en ambas direcciones. Me preguntaba si usar una devolución de llamada en una dirección era ''más limpio''.
Sospecho que hay un patrón de diseño para esto, y humildemente me disculpo por mi ignorancia (y pereza).
Bueno, creo que son las mismas cosas. Hay muchos términos tecnológicos diferentes para nombrar los mismos conceptos o cosas en los diferentes idiomas.
Entonces, ¿qué quiere decir "devolución de llamada" o "controlador de eventos"?
Según MSDN : la función de devolución de llamada es un código dentro de una aplicación administrada que ayuda a una función DLL no administrada a completar una tarea.
Y, MADN también nos da una introducción de la diferencia entre ellos. haga clic aquí
Las devoluciones de llamada son puntos de extensibilidad que permiten que un marco vuelva a llamar al código de usuario a través de un delegado. Estos delegados generalmente se pasan al marco a través de un parámetro de un método.
Los eventos son un caso especial de devoluciones de llamada que admite la sintaxis conveniente y consistente para el suministro del delegado (un controlador de eventos). Además, la finalización de las declaraciones de Visual Studio y los diseñadores brindan ayuda en el uso de API basadas en eventos
Además, en algunos libros, como este libro , el autor parecía decir lo mismo con MSDN.
Por lo tanto, en mi opinión, no puede decir utilizar devoluciones de llamadas en lugar de eventos en el C #.
En general, utilizo una devolución de llamada si es necesario , mientras que un evento se usa cuando debería ser opcional. No expongas un evento si esperas que siempre haya algo escuchando.
Considera lo siguiente:
public class MyClass_Event
{
public event EventHandler MakeMeDoWork;
public void DoWork()
{
if (MakeMeDoWork == null)
throw new Exception("Set the event MakeMeDoWork before calling this method.");
MakeMeDoWork(this, EventArgs.Empty);
}
}
versus:
public class MyClass_Callback
{
public void DoWork(EventHandler callback)
{
if (callback == null)
throw new ArgumentException("Set the callback.", "callback"); // better design
callback(this, EventArgs.Empty);
}
}
El código es casi el mismo ya que la devolución de llamada se puede pasar como nula, pero al menos la excepción lanzada puede ser más relevante.
En términos de diseño OO y acoplamiento de clases, no hay mucha diferencia entre una interfaz de devolución de llamada y un evento.
Sin embargo, prefiero los eventos donde son cosas que la clase necesita para "gritar" a quien esté interesado en escuchar (generalmente varias cosas) y devoluciones de llamada cuando una clase específica ha solicitado una operación asincrónica.
¡Sea lo que sea que uses, úsalos consistentemente a través de la base de código!
Las rellamadas son buenas cuando un objeto desea recibir una sola notificación (por ejemplo, se ejecuta una lectura de datos Async y luego te llama con el resultado).
Los eventos son buenos para notificaciones recurrentes que pueden ser recibidas por un número arbitrario de oyentes.
Un ejemplo es cuando la devolución de llamada debe devolver algo. Ej. (Ejemplo estúpido):
public int Sum(Func<int> callbackA, Func<int> callbackB) {
return callbackA() + callbackB();
}
public void UseSum() {
return sum(() => 10, () => 20);
}
Utilizo las devoluciones de llamada en algunos casos en los que sé que solo se activará una vez, y la devolución de llamada es específica de una llamada a un solo método (en lugar de una instancia de objeto), por ejemplo, como la parte de retorno de un método asíncrono.
Esto es particularmente cierto para los métodos de utilidad estáticos (ya que no tiene una instancia, y los eventos estáticos son mortales cuando se usan de forma descuidada y deben evitarse), pero por supuesto la otra opción es crear una instancia de clase con un evento en su lugar.
Func
o Action
cuando vaya a llamar a la función una vez o use una expresión Lambda.
Los eventos se pueden registrar más de una vez, lo que a veces es ideal. Con una devolución de llamada, uno tiene que implementar un sistema de registro para las devoluciones de llamada si quiere múltiples.