¿Los delegados no son solo interfaces abreviadas?

Supongamos que tenemos:

interface Foo 
{
 bool Func(int x);
}

class Bar: Foo
{
  bool Func(int x)
  {
   return (x>0);
  }  
}

class Baz: Foo
{
  bool Func(int x)
  {
   return (x<0);
  }  
}

Ahora podemos lanzar Bar and Baz como un Foos y llamar a sus métodos Func.

Los delegados simplifican esto un poco:

delegate bool Foo(int x);

bool Bar(int x)
{
 return (x<0);
}

bool Baz(int x)
{
 return (x>0);
}

Ahora podemos lanzar a Bar y Baz como delegados de Foo.

¿Cuál es el beneficio real de los delegados, a excepción de obtener un código más corto?

0
agregado editado
Puntos de vista: 33

10 Respuestas

No, los delegados son para punteros de método. Entonces puede asegurarse de que la firma del método asociado con el delegado sea correcta.

Además, entonces no necesitas saber la estructura de la clase. De esta forma, puede usar un método que haya escrito para pasar a un método en otra clase, y definir la funcionalidad que desea que suceda.

Take a look at the List<> class with the Find method. Now you get to define what determines if something is a match or not, without requiring items contained in the class to implement IListFindable or something similar.

0
agregado

Un delegado es un puntero de método tipeado. Esto le da más flexibilidad que las interfaces porque puede aprovechar la covarianza y la contravarianza, y puede modificar el estado del objeto (tendría que pasar este puntero con funtores basados ​​en la interfaz).

Además, los delegados tienen mucha azúcar sintáctica agradable que te permite hacer cosas como combinarlas fácilmente.

0
agregado

En al menos una propuesta para agregar cierres (es decir, delegados anónimos) a Java, son equivalentes a las interfaces con un método de miembro único.

0
agregado

Uno puede pensar en los delegados como una interfaz para un método que define qué argumentos y qué tipo de retorno debe tener un método para adaptarse al delegado.

0
agregado

Un delegado comparte mucho en común con una referencia de interfaz que tiene un único método desde el punto de vista de la persona que llama.

En el primer ejemplo, Baz y Bar son clases, que se pueden heredar e instanciar. En el segundo ejemplo, Baz y Bar son métodos.

No puede aplicar referencias de interfaz a cualquier clase que coincida con el contrato de interfaz. La clase debe declarar explícitamente que es compatible con la interfaz. Puede aplicar una referencia de delegado a cualquier método que coincida con la firma.

No puede incluir métodos estáticos en el contrato de una interfaz. (Aunque puede atornillar métodos estáticos con métodos de extensión). Puede referirse a métodos estáticos con una referencia de delegado.

0
agregado

Puede pasar delegados como parámetros en funciones (Ok, técnicamente los delegados se convierten en objetos cuando se compilan, pero ese no es el punto aquí). Puede pasar un objeto como parámetro (obviamente), pero luego está vinculando ese tipo de objeto a la función como parámetro. Con los delegados puede pasar cualquier función para ejecutar en el código que tiene la misma firma, independientemente de dónde provenga.

0
agregado

Sí, un delegado puede considerarse una interfaz con un método.

0
agregado

Hay una ligera diferencia, los delegados pueden acceder a las variables miembro de las clases en las que están definidas. En C# (a diferencia de Java), todas las clases internas se consideran estáticas. Por lo tanto, si está utilizando una interfaz para administrar una devolución de llamada, p. un ActionListener para un botón. La clase interna de implementación necesita pasar (a través del constructor) referencias a las partes de la clase contenedora con las que puede necesitar interactuar durante la devolución de llamada. Los delegados no tienen esta restricción, por lo tanto, reduce la cantidad de código requerido para implementar la devolución de llamada.

Un código más breve y conciso también es un beneficio digno.

0
agregado
No son solo variables de miembros. Incluso son variables locales o parámetros del método en el que se define un delegado anónimo.
agregado el autor Daniel Earwicker, fuente
@supercat Correcto. Pero, ¿por qué crees que las interfaces "funcionarán mejor" que los delegados? Un delegado es lógicamente lo mismo que una interfaz de método único.
agregado el autor Daniel Earwicker, fuente
(1) las "instancias de almacenamiento dinámico" no son especialmente costosas en CLR. Consulte smellegantcode.wordpress. com/2009/03/01/& hellip; (2) ¿Qué pasa con delegate void MyDelegate (T v) donde T: IFoo, IBar; ?
agregado el autor Daniel Earwicker, fuente
agregado el autor Daniel Earwicker, fuente
@DanielEarwicker: una rutina en la que se define un cierre define una clase especial "oculta" para contener variables locales, crea una instancia de dicha clase cada vez que esas variables ingresan al alcance, y usa los campos de esa clase en lugar del local "normal" variables. Los escenarios que utilizan cierres funcionarían mejor con las interfaces que con los delegados.
agregado el autor supercat, fuente
@DanielEarwicker: Al menos dos razones: (1) Supongamos que una rutina llamará a una acción en alguna situación, y en esa situación quiero que llame a Foo (x) con el valor presente de la variable local x . Pasar tal solicitud como un delegado requiere la creación de dos instancias de montón: un objeto de clase temporal para mantener x y un delegado para llamarlo. Pasar la solicitud como tipo de interfaz requeriría la creación de una instancia de pila (una implementación de la interfaz). Pasarlo como un genérico restringido por la interf
agregado el autor supercat, fuente
(2) Las interfaces pueden tener métodos genéricos abiertos, mientras que los delegados no pueden. Supongamos que tengo una colección de objetos que no tienen un tipo base común, pero todos son de tipos restringidos para implementar IFoo e IBar. Me gustaría poder ejecutar una rutina suministrada externamente que tome un parámetro limitado a IFoo e IBar. No hay forma de especificar que un delegado tome un parámetro de tipo T: IFoo, IBar pero es posible declarar un método de interfaz que sí lo haga.
agregado el autor supercat, fuente
@DanielEarwicker: El problema es que la rutina que crea el delegado debe especificar el tipo T . Supongamos que una clase tiene una colección de objetos que implementan I1 y I2 , pero no tienen un tipo de base común que implementa ambos; desea exponer un método ForAll . Si tal método aceptara un objeto de tipo interface IActUponConstrained {void Act (T param) donde T: I1, I2;} , podría llamar a Act < T> en cada elemento sin que la entidad que creó el IActUponConstrained te
agregado el autor supercat, fuente
@DanielEarwicker: si no se quiere usar Reflection, la interfaz genérica es necesaria porque los parámetros de tipeo genéricos solo pueden mover "hacia abajo" (más adentro) la pila de llamadas. Una colección de clase abstracta ThingImplementing puede contener instancias de clases derivadas ThingImplementing .WithType donde ActualType: I1, I2 . Cada instancia está vinculada al tipo genérico con el que se creó, pero si se va a llamar a otro código utilizando ese parámetro de tipo, la instancia tendrá que hacer la llamada en sí misma.
agregado el autor supercat, fuente
... ambas interfaces, no hay forma de definir un delegado con un parámetro de tipo restringido tanto para I1 como para I2 , a los cuales un ThingImplementing .WithType podría pasar un Foo , y un ThingImplementing .WithType podría pasar un Bar Sin embargo, se podría definir una implementación de IActUponConstrained , pasarla a ambos objetos, y tener la primera llamada Actuar (Foo param) mientras el segundo llama a Act (Bar
agregado el autor supercat, fuente
@DanielEarwicker: Precisamente. La posibilidad de que el consumidor de un tipo de interfaz suministre un parámetro de tipo genérico es diferente a cualquier cosa que se pueda hacer con los delegados de .net. De hecho, no estoy seguro de que haya una necesidad real de delegados en el marco si los compiladores pudieran hacer para las interfaces genéricas algunas de las cosas que hacen para los delegados (por ejemplo, una interfaz con un Invocar método que tiene una firma determinada, construye una clase cuyo constructor toma dos implementaciones de esa interfaz, y que implementa i
agregado el autor supercat, fuente
... los genéricos no existían en .net 1.0, por lo que los delegados cuasi-genéricos eran una medida provisional necesaria. A propósito, me pregunto por qué los derivados generados por el compilador de Delegate no definen sus propios métodos Combine y Remove específicos del tipo? Tales métodos podrían funcionar correctamente con tipos de delegados contravariantes, mientras que Delegate.Combine no pueden.
agregado el autor supercat, fuente
Es interesante observar que los delegados pueden acceder a los miembros. Un método envuelto en la clase para ser aprobado podría actuar fácilmente como un servicio de mensajería para los datos resultantes, pero el acceso directo es una forma mucho más rápida de hacerlo.
agregado el autor Rick Minerich, fuente

Desde la perspectiva de Ingeniería de Software, tiene razón, los delegados se parecen mucho a las interfaces de funciones, ya que diseñan una interfaz de función.

También se pueden usar de la misma manera: en lugar de pasar toda una clase que contiene el método que necesita, puede pasar solo como delegado. Esto ahorra una gran cantidad de código y crea un código mucho más legible.

Además, con la llegada de las expresiones lambda, ahora también se pueden definir fácilmente al vuelo, lo que es una gran ventaja. Si bien es POSIBLE construir clases sobre la marcha en C#, es realmente un gran dolor en el trasero.

Comparar los dos es un concepto interesante. No había considerado anteriormente lo parecidas que son las ideas desde un punto de vista de uso y estructuración de código.

0
agregado

Las interfaces y los delegados son dos cosas completamente diferentes, aunque entiendo la tentación de describir a los delegados en términos de interfaz para facilitar la comprensión ... sin embargo, no saber la verdad puede conducir a una confusión más adelante.

Los delegados se inspiraron (en parte) debido a que el arte negro de los punteros del método C ++ era inadecuado para ciertos propósitos. Un ejemplo clásico es la implementación de un mecanismo de paso de mensajes o manejo de eventos. Los delegados le permiten definir una firma de método sin ningún conocimiento de los tipos o interfaces de una clase: podría definir un delegado "void eventHandler (Event * e)" e invocarlo en cualquier clase que lo implemente.

Para obtener una idea de este problema clásico, y por qué es deseable que los delegados lean esto y a continuación, this .

0
agregado