2010-09-20 21 views
7

Entiendo que los delegados encapsulan llamadas a métodos. Sin embargo, estoy teniendo dificultades para entender su necesidad. ¿Por qué usar delegados para qué situaciones están diseñadas?¿Cuál es el trato con los delegados?

+7

Cada vez que leo el título de esta pregunta lo escucho en la voz de Jerry Seinfeld. – Jess

Respuesta

11

Un delegado es básicamente un puntero de método. Un delegado nos permite crear una variable de referencia, pero en lugar de referirse a una instancia de una clase, se refiere a un método dentro de la clase. Se refiere a cualquier método que tiene un tipo de devolución y tiene los mismos parámetros especificados por ese delegado. Es un aspecto muy útil del evento. Para una lectura completa, le sugiero que lea el tema en Head First C# (por Andrew Stellman y Jennifer Greene). Explica bellamente el tema del delegado, así como la mayoría de los conceptos en .NET.

+0

awesome description .. –

4

Los delegados se utilizan a menudo para Events. De acuerdo con MSDN, los delegados en .NET están diseñados para la siguiente: se utiliza el patrón de diseño

  • Un concurso completo.
  • Es deseable encapsular un método estático.
  • El llamante no tiene necesidad de acceder a otras propiedades, métodos o interfaces en el objeto que implementa el método.
  • Se desea una composición fácil.
  • Una clase puede necesitar más de una aplicación de la methodimplementation del método

Otra explicación así poner de MSDN,

Un buen ejemplo del uso de una interfaz de solo método en lugar de un delegado es IComparable o IComparable. IComparable declara el método CompareTo de , que devuelve un entero especificando un valor menor que, igual a , o mayor que la relación entre dos objetos del mismo tipo. IComparable se puede utilizar como base de un algoritmo de ordenación, y durante el uso de un método de comparación delegado como base de un algoritmo de ordenación sería válido, no es lo ideal. Debido a que la capacidad de para comparar pertenece a la clase , y el algoritmo de comparación no cambia en tiempo de ejecución, una interfaz de un solo método es ideal. La interfaz de un solo método es ideal.

Desde .NET 2.0 también se ha utilizado para anonymous functions.

Wikipedia tiene una buena explicación sobre el patrón de Delegación,

En la ingeniería de software, el patrón de la delegación es un patrón de diseño en la programación orientada a objetos, donde un objeto, en lugar de realizar una de sus tareas declaradas, los delegados esa tarea a un objeto auxiliar asociado. Pasa la pelota, por así decirlo (técnicamente, una Inversión de Responsabilidad). El objeto auxiliar se llama delegado. El patrón de delegación es uno de los patrones de abstracción fundamentales que subyacen a otros patrones de software, como la composición (también denominada agregación), mixins y aspectos.

8

Bueno, algunos usos comunes:

  • Los controladores de eventos (muy común en el código de interfaz de usuario - "Cuando se hace clic en el botón, quiero este código para ejecutar")
  • devoluciones de llamada de llamadas asíncronas
  • Proporcionar un hilo (o la threadpool) con una nueva tarea para ejecutar
  • Especificación de LINQ proyecciones/condiciones etc

No las considere llamadas de método encapsulante. Piense en ellos como encapsulando un poco de comportamiento/lógica arbitraria con una firma particular. La parte del "método" es algo irrelevante.

Otra forma de pensar en un tipo de delegado es como una interfaz de método único. Un buen ejemplo de esto es la interfaz IComparer<T> y su doble, el tipo de delegado Comparison<T>. Representan la misma idea básica; a veces es más fácil expresar esto como un delegado, y otras veces una interfaz hace la vida más fácil. (Puede escribir fácilmente código para convertir entre los dos, por supuesto.)

0

Los delegados le permiten pasar una referencia a un método. Un ejemplo común es pasar un método de comparación a una función de clasificación.

5

Están diseñados, en términos generales, para cuando tiene un código que sabe que necesitará llamar a otro código, pero no sabe en tiempo de compilación qué otro código podría ser.

Como ejemplo, piense en el botón Windows Forms Button.Click, que utiliza un delegado. Los programadores de Windows Forms saben que querrán que algo suceda cuando se presione ese botón, pero no tienen forma de saber exactamente lo que querrán hacer ... ¡podría ser cualquier cosa!

Así que puede crear un método y asignarlo a un delegado y configurarlo para ese evento, y allí está. Ese es el razonamiento básico para los delegados, aunque hay muchos otros buenos usos para ellos que están relacionados.

0

Si necesita decidir en tiempo de ejecución, qué método utilizar, utilice un delegado. El delegado responderá a alguna acción/evento en tiempo de ejecución y llamará al método apropiado. Es como enviar un "delegado" a una boda a la que no quiere asistir :-)

La gente C lo reconocerá como un puntero a la función, pero no se deje atrapar por la terminología aquí. Todo lo que hace el delegado (y en realidad es un tipo), es proporcionar la firma del método que luego se llamará para implementar la lógica apropiada.

El "Illustrated C#" libro de Dan Solis proporciona el punto de entrada más fácil para el aprendizaje de este concepto que me he encontrado:

http://www.amazon.com/Illustrated-2008-Windows-Net-Daniel-Solis/dp/1590599543

+0

Si bien soy muy aficionado a Apress ... ¿para qué sirve vincular un libro "aún no publicado"? – WernerCD

+0

@WernerCD, gracias por el aviso. Hay dos ediciones anteriores, así que estoy actualizando el enlace :-) – IrishChieftain

+0

Me encanta C# Illustrated 2008 y estoy esperando que se publique 2010 ... fue lo primero que noté heh :) – WernerCD

0

Un delegado es típicamente una combinación de una referencia de objeto y un puntero a uno de los métodos de clase del objeto (los delegados se pueden crear para métodos estáticos, en cuyo caso no hay referencia de objeto). Los delegados pueden invocarse sin tener en cuenta el tipo de objeto incluido, ya que se garantiza que el puntero de método incluido sea válido para el objeto incluido.

Para entender algo de la utilidad detrás de los delegados, piense en el lenguaje C y en la "familia" de funciones printf en C. Suponga que uno quisiera tener una versión de propósito general de "printf" que no solo podría ser se usa como printf, fprintf, sprintf, etc., pero podría enviar su salida a un puerto serie, a un cuadro de texto, a un puerto TCP, a una máquina de glaseado de cookies, o lo que sea, sin tener que preasignar un buffer. Claramente, tal función necesitaría aceptar un puntero de función para la rutina de salida de caracteres, pero eso por sí mismo sería generalmente insuficiente.

Una implementación típica (desafortunadamente no estandarizada) tendrá una rutina gp_printf de uso general que acepta (además de la cadena de formato y los parámetros de salida) un puntero void y un puntero a una función que acepta un carácter y un vacío puntero. La rutina gp_printf no usará el puntero de vacío pasado para ningún propósito en sí mismo, sino que lo pasará a la función de salida de caracteres. Esa función puede entonces convertir el puntero en un ARCHIVO * (si fprintf llama a gp_printf), o un char ** (si lo está llamando sprintf), o un SERIAL_PORT * (si lo está llamando serial_printf), o lo que sea .

Tenga en cuenta que debido a que cualquier tipo de información podría pasar por el vacío *, no habría límite en lo que podría hacer gp_printf. Sin embargo, habría un peligro: si la información pasada en el vacío * no es lo que la función espera, es probable que se produzca un Comportamiento Indefinido (es decir, cosas potencialmente muy malas). Sería responsabilidad de la persona que llama asegurarse de que la función puntero y void * estén emparejados correctamente; nada en el sistema protegería contra el uso incorrecto.

En .net, un delegado proporcionaría la funcionalidad combinada de la función puntero y void * arriba, con la ventaja añadida de que el constructor del delegado se aseguraría de que los datos fueran del tipo adecuado para la función. Una característica práctica.

1

Oversimplified: Yo diría que un delegado es un marcador de posición para una función hasta ese momento cuando algo le asigna una función real al delegado. Llamar a delegados no asignados arroja una excepción.

La confusión se produce porque a menudo se hace poca diferencia entre la definición, declaración, creación de instancias y la invocación de delegados.

Definición:
poner esto en un espacio de nombres como lo haría con cualquier clase de definición.

public delegate bool DoSomething(string withThis); 

Esto es comparable a una clase de definición en que ahora se puede declarar variables de este delegado.

Declaración:
Ponga esta es una de las rutinas de función como lo haría declarar cualquier variable.

DoSomething doSth; 

de instancias y asignación:
Por lo general, que va a hacer esto junto con la declaración.

doSth = new DoSomething(MyDoSomethingFunc); 

El "nuevo DoSomething (..)" es la creación de instancias. El doSth = ... es la tarea.

Tenga en cuenta que ya debe haber definido una función llamada "MyDoSomething" que toma una cadena y devuelve un bool.

Luego puede invocar la función.

Invocación:

bool result = doSth(myStringValue); 

Eventos:
se puede ver que los acontecimientos vienen en:

Puesto que un miembro de una clase es por lo general una declaración basada en una definición.
Como

class MyClass { 
    private int MyMember; 
} 

Un evento es una declaración sobre la base de un delegado:

public delegate bool DoSomething(string withWhat); 

class MyClass { 
    private event DoSomething MyEvent; 
} 

La diferencia con el ejemplo anterior es que los eventos son "especiales":

  1. Puede llamar eventos no asignados sin lanzar una excepción.
  2. Puede asignar múltiples funciones a un evento. Entonces todos serán llamados secuencialmente. Si una de esas llamadas arroja una excepción, el resto no puede jugar.

Son realmente azúcar sintáctica para matrices de delegados.

El punto es, por supuesto, que algo/alguien más hará la asignación por usted.

Cuestiones relacionadas