¿Te refieres a delegados en el sistema de tipos .NET o la sintaxis de delegado de C #? ¿Quiere decir "cuándo usa la sintaxis de delegado en lugar de la sintaxis de expresión lambda" o quiere decir "cuándo usa delegados en lugar de clases / interfaces / métodos virtuales / etc."?
Ahora que tenemos expresiones lambda y métodos anónimos en C #, uso delegados mucho más. En C # 1, donde siempre tenía que tener un método separado para implementar la lógica, usar un delegado a menudo no tenía sentido. En estos días utilizo delegados para:
Controladores de eventos (para GUI y más)
Hilos iniciales
Devoluciones de llamada (por ejemplo, para API asíncronas)
LINQ y similares (List.Find, etc.)
En cualquier otro lugar donde quiera aplicar efectivamente el código de "plantilla" con alguna lógica especializada en el interior (donde el delegado proporciona la especialización)
No estoy seguro de cómo lo explicaría brevemente sin hacer las cosas más confusas :) (¡Podría decirse que está cubierto por los controladores de eventos, LINQ y el bit de plantilla de todos modos!
Jon Skeet
1
Tu primera oración no tiene mucho sentido.
senfo
3
Sé lo que está intentando decir, pero lo siguiente me resultaría más fácil de leer: "Ahora que tenemos expresiones lambda y métodos anónimos en C #, utilizo muchos más delegados". Sé que soy quisquilloso, pero realmente tuve que leer esa oración varias veces antes de que tuviera sentido para mí.
senfo
4
+1 por atreverse a desafiar al venerable Mr Skeet ;-)
indra
29
Los delegados son muy útiles para muchos propósitos.
Uno de esos propósitos es utilizarlos para filtrar secuencias de datos. En este caso, usaría un predicado delegado que acepta un argumento y devuelve verdadero o falso dependiendo de la implementación del propio delegado.
Aquí hay un ejemplo tonto: estoy seguro de que puede extrapolar algo más útil de esto:
using System;
using System.Linq;
using System.Collections.Generic;classProgram{staticvoidMain(){List<String> names =newList<String>{"Nicole Hare","Michael Hare","Joe Hare","Sammy Hare","George Washington",};// Here I am passing "inMyFamily" to the "Where" extension method// on my List<String>. The C# compiler automatically creates // a delegate instance for me.IEnumerable<String> myFamily = names.Where(inMyFamily);foreach(String name in myFamily)Console.WriteLine(name);}staticBoolean inMyFamily(String name){return name.EndsWith("Hare");}}
El static Boolean inMyFamily(String name)método es el delegado. Donde toma un delegado como parámetro. Dado que los delegados son solo punteros de función cuando pasa el nombre del método al .Where(delegate)que se convierte en el delegado. Dado que inMyFamily devuelve un tipo booleano, en realidad se considera un predicado. Los predicados son solo delegados que devuelven booleanos.
Landon Poch
4
"Los predicados son simplemente delegados que devuelven booleanos". +1
daehaai
@LandonPoch, ese comentario habría estado mejor en la respuesta. yo, siendo un principiante, no podía distinguir dónde estaba. Gracias.
Eakan Gopalakrishnan
@Eakan, realmente no estaba respondiendo la pregunta principal (cuándo usarías delegados), así que lo dejé como comentario.
Landon Poch
14
Encontré otra respuesta interesante:
Un compañero de trabajo me acaba de hacer esta pregunta: ¿qué sentido tienen los delegados en .NET? Mi respuesta fue muy corta y una que no había encontrado en línea: retrasar la ejecución de un método.
Puede utilizar delegados para declarar variables y parámetros con tipo de función.
Ejemplo
Considere el patrón de "préstamo de recursos". Desea controlar la creación y limpieza de un recurso, mientras permite que el código del cliente "tome prestado" el recurso en el medio.
Cualquier método que coincida con esta firma se puede utilizar para crear una instancia de un delegado de este tipo. En C # 2.0, esto se puede hacer implícitamente, simplemente usando el nombre del método, así como también usando métodos anónimos.
Este método utiliza el tipo como parámetro. Tenga en cuenta la invocación del delegado.
publicclassDataProvider{protectedstring _connectionString;publicDataProvider(string psConnectionString ){
_connectionString = psConnectionString;}publicvoidUseReader(string psSELECT,DataReaderUser readerUser ){
using (SqlConnection connection =newSqlConnection( _connectionString ))try{SqlCommand command =newSqlCommand( psSELECT, connection );
connection.Open();SqlDataReader reader = command.ExecuteReader();while( reader.Read())
readerUser( reader );// the delegate is invoked}catch(System.Exception ex ){// handle exceptionthrow ex;}}}
La función se puede llamar con un método anónimo de la siguiente manera. Tenga en cuenta que el método anónimo puede utilizar variables declaradas fuera de sí mismo. Esto es extremadamente útil (aunque el ejemplo es un poco artificial).
string sTableName ="test";string sQuery ="SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='"+ sTableName +"'";DataProvider.UseReader( sQuery,delegate(System.Data.IDataReader reader ){Console.WriteLine( sTableName +"."+ reader[0]);});
Los delegados a menudo se pueden utilizar en lugar de una interfaz con un método; un ejemplo común de esto sería el patrón de observadores. En otros idiomas, si desea recibir una notificación de que ha sucedido algo, puede definir algo como:
classIObserver{voidNotify(...);}
En C #, esto se expresa más comúnmente mediante eventos, donde el controlador es un delegado, por ejemplo:
Llego muy tarde, pero estaba teniendo problemas para descifrar el propósito de los delegados hoy y escribí dos programas simples que dan el mismo resultado que creo que explica bien su propósito.
NoDelegates.cs
using System;publicclassTest{publicconstint MAX_VALUE =255;publicconstint MIN_VALUE =10;publicstaticvoid checkInt(int a){Console.Write("checkInt result of {0}: ", a);if(a < MAX_VALUE && a > MIN_VALUE)Console.WriteLine("max and min value is valid");elseConsole.WriteLine("max and min value is not valid");}publicstaticvoid checkMax(int a){Console.Write("checkMax result of {0}: ", a);if(a < MAX_VALUE)Console.WriteLine("max value is valid");elseConsole.WriteLine("max value is not valid");}publicstaticvoid checkMin(int a){Console.Write("checkMin result of {0}: ", a);if(a > MIN_VALUE)Console.WriteLine("min value is valid");elseConsole.WriteLine("min value is not valid");Console.WriteLine("");}}publicclassDriver{publicstaticvoidMain(string[] args){Test.checkInt(1);Test.checkMax(1);Test.checkMin(1);Test.checkInt(10);Test.checkMax(10);Test.checkMin(10);Test.checkInt(20);Test.checkMax(20);Test.checkMin(20);Test.checkInt(30);Test.checkMax(30);Test.checkMin(30);Test.checkInt(254);Test.checkMax(254);Test.checkMin(254);Test.checkInt(255);Test.checkMax(255);Test.checkMin(255);Test.checkInt(256);Test.checkMax(256);Test.checkMin(256);}}
Delegates.cs
using System;publicdelegatevoidValid(int a);publicclassTest{publicconstint MAX_VALUE =255;publicconstint MIN_VALUE =10;publicstaticvoid checkInt(int a){Console.Write("checkInt result of {0}: ", a);if(a < MAX_VALUE && a > MIN_VALUE)Console.WriteLine("max and min value is valid");elseConsole.WriteLine("max and min value is not valid");}publicstaticvoid checkMax(int a){Console.Write("checkMax result of {0}: ", a);if(a < MAX_VALUE)Console.WriteLine("max value is valid");elseConsole.WriteLine("max value is not valid");}publicstaticvoid checkMin(int a){Console.Write("checkMin result of {0}: ", a);if(a > MIN_VALUE)Console.WriteLine("min value is valid");elseConsole.WriteLine("min value is not valid");Console.WriteLine("");}}publicclassDriver{publicstaticvoidMain(string[] args){Valid v1 =newValid(Test.checkInt);
v1 +=newValid(Test.checkMax);
v1 +=newValid(Test.checkMin);
v1(1);
v1(10);
v1(20);
v1(30);
v1(254);
v1(255);
v1(256);}}
Un uso ligeramente diferente es acelerar la reflexión; es decir, en lugar de usar la reflexión cada vez, puede usarDelegate.CreateDelegate para crear un delegado (escrito) a un método (a MethodInfo) y llamar a ese delegado en su lugar. Esto es mucho más rápido por llamada, ya que las comprobaciones ya se han realizado.
Con Expression, también puede hacer lo mismo para crear código sobre la marcha; por ejemplo, puede crear fácilmente un Expressionque represente el operador + para un tipo elegido en tiempo de ejecución (para proporcionar soporte de operador para genéricos, que el lenguaje no proporciona) ; y puede compilar un Expressiondelegado escrito: trabajo hecho.
Los delegados se utilizan cada vez que se utilizan eventos; ese es el mecanismo mediante el cual funcionan.
Además, los delegados son muy útiles para cosas como el uso de consultas LINQ. Por ejemplo, muchas consultas LINQ toman un delegado (a menudo Func<T,TResult>) que se puede usar para filtrar.
Un ejemplo podría ser el que se ve aquí . Tiene un método para procesar un objeto que cumple con ciertos requisitos. Sin embargo, desea poder procesar el objeto de varias formas. En lugar de tener que crear métodos separados, simplemente puede asignar un método coincidente que procese el objeto a un delegado y pasar el delegado al método que selecciona los objetos. De esa forma, puede asignar diferentes métodos al método de un selector. Traté de hacer esto fácilmente comprensible.
Por ejemplo, podría tener una aplicación de formularios win que descarga un archivo. La aplicación inicia un hilo de trabajo para realizar la descarga (lo que evita que la GUI se bloquee). El hilo de trabajo utiliza delegados para enviar mensajes de estado (por ejemplo, el progreso de la descarga) al programa principal, de modo que la GUI pueda actualizar la barra de estado.
La primera línea de uso es reemplazar el patrón Observador / Observable (eventos). El segundo, una bonita y elegante versión del patrón de estrategia. Se pueden reunir varios otros usos, aunque creo que son más esotéricos que estos dos primeros.
En cualquier momento que desee encapsular el comportamiento, pero invocarlo de manera uniforme. Controladores de eventos, funciones de devolución de llamada, etc. Puede lograr cosas similares utilizando interfaces y conversiones, pero a veces, el comportamiento no está necesariamente ligado a un tipo u objeto . A veces solo tienes un comportamiento que necesitas encapsular.
¡Inicialización perezosa de parámetros! Además de todas las respuestas anteriores (patrón de estrategia, patrón de observador, etc.), los delegados le permiten manejar la inicialización diferida de parámetros. Por ejemplo, suponga que tiene una función Download () que lleva bastante tiempo y devuelve un determinado DownloadedObject. Este objeto es consumido por un Almacenamiento dependiendo de ciertas Condiciones. Normalmente, haría lo siguiente:
storage.Store(conditions,Download(item))
Sin embargo, con los delegados (más precisamente, lambdas) puede hacer lo siguiente, cambiando la firma de la tienda para que reciba una Condición y un Func <Item, DownloadedObject> y usarlo así:
storage.Store(conditions,(item)=>Download(item))
Por tanto, el almacenamiento solo evaluará al delegado si es necesario, ejecutando la descarga en función de las Condiciones.
Punto menor, pero "más precisamente, lambdas": podría hacer lo mismo con un método anónimo en C # 2.0, aunque sería más detallado: delegate (ItemType item) {[return] Download (item);}
Marc Gravell
Claro, igual que LINQ: las lambdas no son más que azúcar sintáctico para los delegados. Simplemente hicieron que los delegados fueran más accesibles.
Santiago Palladino
Las lambdas son algo más que simples delegados, ya que se pueden convertir en árboles de expresión y en delegados.
Jon Skeet
Bueno, las lambdas también se pueden compilar en Expressions, que son completamente diferentes de los delegados. Pero su ejemplo usó Func <,>, que se puede usar desde un método anon. Las expresiones serían extremadamente dolorosas de escribir en C # 2.0.
Hasta donde yo sé, los delegados se pueden convertir en punteros de función. Esto hace la vida MUCHO más fácil cuando se interopera con código nativo que toma punteros de función, ya que pueden estar orientados a objetos de manera efectiva, aunque el programador original no hizo ninguna provisión para que eso suceda.
Respuestas:
Ahora que tenemos expresiones lambda y métodos anónimos en C #, uso delegados mucho más. En C # 1, donde siempre tenía que tener un método separado para implementar la lógica, usar un delegado a menudo no tenía sentido. En estos días utilizo delegados para:
fuente
Los delegados son muy útiles para muchos propósitos.
Uno de esos propósitos es utilizarlos para filtrar secuencias de datos. En este caso, usaría un predicado delegado que acepta un argumento y devuelve verdadero o falso dependiendo de la implementación del propio delegado.
Aquí hay un ejemplo tonto: estoy seguro de que puede extrapolar algo más útil de esto:
fuente
static Boolean inMyFamily(String name)
método es el delegado. Donde toma un delegado como parámetro. Dado que los delegados son solo punteros de función cuando pasa el nombre del método al.Where(delegate)
que se convierte en el delegado. Dado que inMyFamily devuelve un tipo booleano, en realidad se considera un predicado. Los predicados son solo delegados que devuelven booleanos.Encontré otra respuesta interesante:
Fuente: LosTechies
Como lo está haciendo LINQ.
fuente
Puede utilizar delegados para declarar variables y parámetros con tipo de función.
Ejemplo
Considere el patrón de "préstamo de recursos". Desea controlar la creación y limpieza de un recurso, mientras permite que el código del cliente "tome prestado" el recurso en el medio.
Esto declara un tipo de delegado.
Cualquier método que coincida con esta firma se puede utilizar para crear una instancia de un delegado de este tipo. En C # 2.0, esto se puede hacer implícitamente, simplemente usando el nombre del método, así como también usando métodos anónimos.
Este método utiliza el tipo como parámetro. Tenga en cuenta la invocación del delegado.
La función se puede llamar con un método anónimo de la siguiente manera. Tenga en cuenta que el método anónimo puede utilizar variables declaradas fuera de sí mismo. Esto es extremadamente útil (aunque el ejemplo es un poco artificial).
fuente
Los delegados a menudo se pueden utilizar en lugar de una interfaz con un método; un ejemplo común de esto sería el patrón de observadores. En otros idiomas, si desea recibir una notificación de que ha sucedido algo, puede definir algo como:
En C #, esto se expresa más comúnmente mediante eventos, donde el controlador es un delegado, por ejemplo:
Otro gran lugar para usar delegados si tiene que pasar un predicado a una función, por ejemplo, al seleccionar un conjunto de elementos de una lista:
Lo anterior es un ejemplo de la sintaxis lambda, que también podría haberse escrito de la siguiente manera:
Otro lugar donde puede ser útil utilizar delegados es para registrar funciones de fábrica, por ejemplo:
¡Espero que esto ayude!
fuente
Llego muy tarde, pero estaba teniendo problemas para descifrar el propósito de los delegados hoy y escribí dos programas simples que dan el mismo resultado que creo que explica bien su propósito.
NoDelegates.cs
Delegates.cs
fuente
Un uso ligeramente diferente es acelerar la reflexión; es decir, en lugar de usar la reflexión cada vez, puede usar
Delegate.CreateDelegate
para crear un delegado (escrito) a un método (aMethodInfo
) y llamar a ese delegado en su lugar. Esto es mucho más rápido por llamada, ya que las comprobaciones ya se han realizado.Con
Expression
, también puede hacer lo mismo para crear código sobre la marcha; por ejemplo, puede crear fácilmente unExpression
que represente el operador + para un tipo elegido en tiempo de ejecución (para proporcionar soporte de operador para genéricos, que el lenguaje no proporciona) ; y puede compilar unExpression
delegado escrito: trabajo hecho.fuente
Los delegados se utilizan cada vez que se utilizan eventos; ese es el mecanismo mediante el cual funcionan.
Además, los delegados son muy útiles para cosas como el uso de consultas LINQ. Por ejemplo, muchas consultas LINQ toman un delegado (a menudo
Func<T,TResult>
) que se puede usar para filtrar.fuente
suscribiendo eventhandlers a eventos
fuente
Un ejemplo podría ser el que se ve aquí . Tiene un método para procesar un objeto que cumple con ciertos requisitos. Sin embargo, desea poder procesar el objeto de varias formas. En lugar de tener que crear métodos separados, simplemente puede asignar un método coincidente que procese el objeto a un delegado y pasar el delegado al método que selecciona los objetos. De esa forma, puede asignar diferentes métodos al método de un selector. Traté de hacer esto fácilmente comprensible.
fuente
Utilizo delegados para comunicarme con hilos.
Por ejemplo, podría tener una aplicación de formularios win que descarga un archivo. La aplicación inicia un hilo de trabajo para realizar la descarga (lo que evita que la GUI se bloquee). El hilo de trabajo utiliza delegados para enviar mensajes de estado (por ejemplo, el progreso de la descarga) al programa principal, de modo que la GUI pueda actualizar la barra de estado.
fuente
Para el controlador de eventos
Para pasar un método en los parámetros de un método
fuente
La primera línea de uso es reemplazar el patrón Observador / Observable (eventos). El segundo, una bonita y elegante versión del patrón de estrategia. Se pueden reunir varios otros usos, aunque creo que son más esotéricos que estos dos primeros.
fuente
Eventos, otras operaciones anynch
fuente
En cualquier momento que desee encapsular el comportamiento, pero invocarlo de manera uniforme. Controladores de eventos, funciones de devolución de llamada, etc. Puede lograr cosas similares utilizando interfaces y conversiones, pero a veces, el comportamiento no está necesariamente ligado a un tipo u objeto . A veces solo tienes un comportamiento que necesitas encapsular.
fuente
¡Inicialización perezosa de parámetros! Además de todas las respuestas anteriores (patrón de estrategia, patrón de observador, etc.), los delegados le permiten manejar la inicialización diferida de parámetros. Por ejemplo, suponga que tiene una función Download () que lleva bastante tiempo y devuelve un determinado DownloadedObject. Este objeto es consumido por un Almacenamiento dependiendo de ciertas Condiciones. Normalmente, haría lo siguiente:
Sin embargo, con los delegados (más precisamente, lambdas) puede hacer lo siguiente, cambiando la firma de la tienda para que reciba una Condición y un Func <Item, DownloadedObject> y usarlo así:
Por tanto, el almacenamiento solo evaluará al delegado si es necesario, ejecutando la descarga en función de las Condiciones.
fuente
Uso de delegados
fuente
El parámetro de comparación en In Array.Sort (matriz T [], comparación de comparación), List.Sort (comparación de comparación), etc.
fuente
Hasta donde yo sé, los delegados se pueden convertir en punteros de función. Esto hace la vida MUCHO más fácil cuando se interopera con código nativo que toma punteros de función, ya que pueden estar orientados a objetos de manera efectiva, aunque el programador original no hizo ninguna provisión para que eso suceda.
fuente
Los delegados se utilizan para llamar a un método por su referencia. Por ejemplo:
fuente