2010-01-07 14 views
28

Perdóname si esta es una pregunta estúpida, admito que no he pensado demasiado.Cuándo utilizar devoluciones de llamada en lugar de eventos en C#?

¿Cuándo preferiría usar una devolución de llamada (es decir, pasar un Func o una acción), en lugar de exponer y usar un evento?

ACTUALIZACIÓN

¿Qué motivó a esta pregunta fue el siguiente problema:

tengo una clase ThingsHandler, que se puede asociar con un ThingEditor. El ThingsHandler maneja una lista de cosas, conoce su orden, cuál es el 'actual', cuando se añaden o eliminan los nuevos etc.

Los ThingEditors solo puede modificar una sola cosa.

El ThingsHandler necesita para alertar la ThingEditor cuando el usuario selecciona una cosa nueva para editar, y la ThingEditor necesita para alertar al ThingsHandler cuando el usuario dice 'Hecho'.

Lo que me molesta es que estas dos clases tengan referencias entre sí, aunque supongo que es inevitable, o vinculantes para los 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).

Respuesta

23

utilizo devoluciones de llamada en algunos casos en los que conozco sólo vez se disparará una vez, y la devolución de llamada es específica a una sola llamada al método (en lugar de a una instancia de objeto) - por ejemplo, como el regreso parte 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 sin cuidado, y deben evitarse), pero por supuesto la otra opción es crear una instancia de clase con un evento en su lugar.

8

Las devoluciones de llamada son válidas cuando un objeto desea recibir una sola notificación (por ejemplo, se ejecuta una lectura de datos Async y luego se le llama con el resultado).

Los eventos son buenos para las notificaciones recurrentes que pueden ser recibidas por un número arbitrario de oyentes.

3

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 donde una clase específica ha solicitado una operación asincrónica.

¡Cualquiera que sea su uso, úselos uniformemente en la base de código!

4

Usaría Func o Action cuando voy a llamar a la función una vez o uso 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.

3

Un ejemplo es cuando la devolución de llamada debería devolver algo. P.ej. (Ejemplo estúpida):

public int Sum(Func<int> callbackA, Func<int> callbackB) { 
    return callbackA() + callbackB(); 
} 

public void UseSum() { 
    return sum(() => 10,() => 20); 
} 
21

Generalmente, utilizo una devolución de llamada si es requiere, mientras que un evento se utiliza cuando debería ser opcional. No exponga un evento si espera que siempre haya algo escuchando.

considerar 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); 
    } 
} 

frente:

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 la misma que la devolución de llamada se pueden pasar como nula, pero al menos la excepción lanzada puede ser más relevante.

25

Aunque las otras respuestas hasta ahora parecen razonables, tomaría un enfoque más filosófico.

Una clase es un mecanismo que los modelos un determinado tipo de cosas 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 modelada. Un breve ejemplo de lo que quiero decir:

class Giraffe : Mammal, IDisposable 
{ 
    public override void Eat(Food f) { ... } 
    public void Dispose() { ... } 
} 

Aviso cómo hemos fusionado la cosa en el mundo real que está siendo modelado (una jirafa es una especie de mamífero, una jirafa come alimentos) con los detalles de la aplicación (una ejemplo de Jirafa es un objeto que puede eliminarse con la instrucción "usar"). 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 utilizar eventos (y propiedades) como parte de los métodos semánticos modelo y el uso de devolución de llamada (y campos) como parte del mecanismo de. 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.

+2

Maldición, estás haciendo que mi cerebro duela otra vez :) En mi código actual, las clases están tan alejadas de todo en el mundo real, no estoy seguro de dónde comenzaría ... (ver la pregunta actualizada) – Benjol

+3

Buena respuesta, y para resumir: los eventos son notificaciones (dieron a luz), las devoluciones de llamada son solicitudes (comer alimentos). Relacionando esto con el mundo real y obtienes un diagrama de caso de uso con la jirafa como el objeto, y el usuario como el manejador de animales (o un niño con palomitas arrojándolo a través de las barras) – Codesleuth

+0

No solo es difícil de digerir, sino también difícil de digerir traducir ... ¡Finalmente vale la pena! Gracias Eric. –

0

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.click here

Las devoluciones 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 conclusión y los diseñadores de instrucción de Visual Studio proporcionan ayuda en el uso de las API basadas en eventos

Además, en algunos libros, tales como this book, el autor parecía decir lo mismo con MSDN.

Por lo tanto, en mi opinión, no puede decir utilizar devoluciones de llamada en lugar de eventos en el C#.

Cuestiones relacionadas