Você quer dizer delegados no sistema de tipo .NET ou a sintaxe de delegado C #? Você quer dizer "quando você usa a sintaxe de delegado em vez da sintaxe de expressão lambda" ou "quando você usa delegados em vez de classes / interfaces / métodos virtuais / etc."?
Agora que temos expressões lambda e métodos anônimos em C #, uso muito mais delegados. Em C # 1, onde você sempre tinha que ter um método separado para implementar a lógica, usar um delegado geralmente não fazia sentido. Hoje em dia eu uso delegados para:
Manipuladores de eventos (para GUI e mais)
Tópicos iniciais
Callbacks (por exemplo, para APIs assíncronas)
LINQ e semelhantes (List.Find etc)
Em qualquer outro lugar onde eu queira aplicar efetivamente o código de "modelo" com alguma lógica especializada interna (onde o delegado fornece a especialização)
Não tenho certeza de como explicaria isso resumidamente, sem tornar as coisas mais confusas :) (Provavelmente, ele é coberto por manipuladores de eventos, LINQ e a parte templaty de qualquer maneira!
Jon Skeet
1
Sua primeira frase não faz muito sentido.
senfo
3
Sei o que você está tentando dizer, mas acho o seguinte mais fácil de ler: "Agora que temos expressões lambda e métodos anônimos em C #, uso delegados muito mais." Eu sei que estou minando, mas eu realmente tive que ler essa frase algumas vezes antes de fazer sentido para mim.
senfo
4
+1 por ousar desafiar o venerável Sr. Skeet ;-)
indra
29
Os delegados são muito úteis para muitos propósitos.
Uma dessas finalidades é usá-los para filtrar sequências de dados. Nesse caso, você usaria um delegado de predicado que aceita um argumento e retorna verdadeiro ou falso, dependendo da implementação do próprio delegado.
Aqui está um exemplo bobo - tenho certeza de que você pode extrapolar algo mais útil com isso:
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");}}
O static Boolean inMyFamily(String name)método é o delegado. Onde leva um delegado como parâmetro. Uma vez que delegados são apenas ponteiros de função quando você passa o nome do método para o .Where(delegate)que se torna o delegado. Como inMyFamily retorna um tipo booleano, ele é considerado um predicado. Predicados são apenas delegados que retornam booleanos.
Landon Poch de
4
"Predicados são apenas delegados que retornam booleanos." +1
daehaai
@LandonPoch esse comentário teria sido melhor colocado na resposta. eu, sendo um iniciante, não conseguia descobrir onde era. obrigado.
Eakan Gopalakrishnan
@Eakan, eu não estava realmente respondendo à pergunta principal (quando você usaria delegados), então deixei como um comentário.
Landon Poch
14
Encontrei outra resposta interessante:
Um colega de trabalho acabou de me fazer esta pergunta - qual é o ponto de delegados em .NET? Minha resposta foi muito curta e que ele não encontrou online: atrasar a execução de um método.
+ 1..não tinha pensado nisso dessa forma. Bom ponto
Luke101
12
Você pode usar delegados para declarar variáveis e parâmetros com tipo de função.
Exemplo
Considere o padrão de "empréstimo de recursos". Você deseja controlar a criação e a limpeza de um recurso, enquanto permite que o código do cliente "pegue emprestado" o recurso intermediário.
Qualquer método que corresponda a essa assinatura pode ser usado para instanciar um delegado desse tipo. No C # 2.0, isso pode ser feito implicitamente, simplesmente usando o nome do método, bem como usando métodos anônimos.
Este método usa o tipo como parâmetro. Observe a invocação do 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;}}}
A função pode ser chamada com um método anônimo da seguinte maneira. Observe que o método anônimo pode usar variáveis declaradas fora de si mesmo. Isso é extremamente útil (embora o exemplo seja um pouco 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]);});
Os delegados geralmente podem ser usados no lugar de uma interface com um método; um exemplo comum disso seria o padrão do observador. Em outros idiomas, se você deseja receber uma notificação de que algo aconteceu, você pode definir algo como:
classIObserver{voidNotify(...);}
Em C #, isso é mais comumente expresso por meio de eventos, em que o manipulador é um delegado, por exemplo:
Outro ótimo lugar para usar delegados se você precisar passar um predicado para uma função, por exemplo, ao selecionar um conjunto de itens de uma lista:
myList.Where(i => i >10);
A descrição acima é um exemplo da sintaxe lambda, que também poderia ter sido escrita da seguinte maneira:
myList.Where(delegate(int i){return i >10;});
Outro lugar onde pode ser útil usar delegados é registrar funções de fábrica, por exemplo:
Estou chegando nisso muito tarde, mas estava tendo problemas para descobrir o propósito dos delegados hoje e escrevi dois programas simples que fornecem a mesma saída que eu acho que explica bem seu 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);}}
Um uso ligeiramente diferente é acelerar a reflexão; isto é, em vez de usar reflexão a cada vez, você pode usar Delegate.CreateDelegatepara criar um delegado (digitado) para um método (a MethodInfo) e chamar esse delegado. Isso é muito mais rápido por chamada, pois as verificações já foram feitas.
Com o Expression, você também pode fazer o mesmo para criar código em tempo real - por exemplo, você pode criar facilmente um Expressionque representa o operador + para um tipo escolhido em tempo de execução (para fornecer suporte de operador para genéricos, que a linguagem não fornece) ; e você pode compilar um Expressionpara um delegado digitado - trabalho feito.
Os delegados são usados sempre que você usa eventos - esse é o mecanismo pelo qual eles funcionam.
Além disso, os delegados são muito úteis para coisas como o uso de consultas LINQ. Por exemplo, muitas consultas LINQ usam um delegado (frequentemente Func<T,TResult>) que pode ser usado para filtragem.
Um exemplo pode ser visto aqui . Você tem um método para processar um objeto que atende a certos requisitos. No entanto, você deseja processar o objeto de várias maneiras. Em vez de criar métodos separados, você pode simplesmente atribuir um método de correspondência que processa o objeto a um delegado e passa o delegado ao método que seleciona os objetos. Dessa forma, você pode atribuir métodos diferentes para o método de um seletor. Tentei tornar isso facilmente compreensível.
Por exemplo, posso ter um aplicativo win forms que baixa um arquivo. O aplicativo inicia um thread de trabalho para fazer o download (o que evita que a GUI seja travada). O thread de trabalho usa delegados para enviar mensagens de status (por exemplo, progresso do download) de volta ao programa principal, para que a GUI possa atualizar a barra de status.
A primeira linha de uso é substituir o padrão Observer / Observable (eventos). A segunda, uma versão elegante e agradável do padrão Strategy. Vários outros usos podem ser reunidos, embora mais esotéricos do que esses dois primeiros, eu acho.
Sempre que quiser encapsular o comportamento, mas invoque-o de maneira uniforme. Manipuladores de eventos, funções de retorno de chamada, etc. Você pode realizar coisas semelhantes usando interfaces e casts, mas às vezes, o comportamento não está necessariamente vinculado a um tipo ou objeto . Às vezes, você apenas tem um comportamento que precisa encapsular.
Inicialização de parâmetro lenta! Além de todas as respostas anteriores (padrão de estratégia, padrão de observador, etc.), os delegados permitem que você lide com a inicialização lenta de parâmetros. Por exemplo, suponha que você tenha uma função Download () que leva muito tempo e retorna um certo DownloadedObject. Este objeto é consumido por um Storage dependendo de certas Condições. Normalmente, você:
storage.Store(conditions,Download(item))
Porém, com delegados (mais precisamente, lambdas) você pode fazer o seguinte, alterando a assinatura da loja para que ela receba uma Condição e uma Func <Item, DownloadedObject> e use-a assim:
storage.Store(conditions,(item)=>Download(item))
Portanto, o storage só avaliará o delegado se necessário, executando o download dependendo das condições.
Ponto menor, mas re "mais precisamente, lambdas" - você poderia fazer o mesmo com um método anônimo em C # 2.0, embora fosse mais prolixo: delegate (ItemType item) {[return] Download (item);}
Marc Gravell
Claro, o mesmo que LINQ: lambdas nada mais são do que açúcar sintático para delegados. Eles apenas tornaram os delegados mais acessíveis.
Santiago Palladino
Lambdas são um pouco mais do que apenas delegados, já que são conversíveis em árvores de expressão e também em delegados.
Jon Skeet,
Bem, lambdas também podem ser compilados em Expressões, que são completamente diferentes dos delegados. Mas seu exemplo usou Func <,>, que pode ser usado a partir de um método anon. Seria extremamente difícil escrever expressões em C # 2.0.
Pelo que eu sei, os delegados podem ser convertidos em ponteiros de função. Isso torna a vida MUITO mais fácil ao interoperar com código nativo que usa ponteiros de função, pois eles podem ser efetivamente orientados a objetos, mesmo que o programador original não tenha feito nenhuma provisão para que isso aconteça.
Respostas:
Agora que temos expressões lambda e métodos anônimos em C #, uso muito mais delegados. Em C # 1, onde você sempre tinha que ter um método separado para implementar a lógica, usar um delegado geralmente não fazia sentido. Hoje em dia eu uso delegados para:
fonte
Os delegados são muito úteis para muitos propósitos.
Uma dessas finalidades é usá-los para filtrar sequências de dados. Nesse caso, você usaria um delegado de predicado que aceita um argumento e retorna verdadeiro ou falso, dependendo da implementação do próprio delegado.
Aqui está um exemplo bobo - tenho certeza de que você pode extrapolar algo mais útil com isso:
fonte
static Boolean inMyFamily(String name)
método é o delegado. Onde leva um delegado como parâmetro. Uma vez que delegados são apenas ponteiros de função quando você passa o nome do método para o.Where(delegate)
que se torna o delegado. Como inMyFamily retorna um tipo booleano, ele é considerado um predicado. Predicados são apenas delegados que retornam booleanos.Encontrei outra resposta interessante:
Fonte: LosTechies
Exatamente como o LINQ está fazendo.
fonte
Você pode usar delegados para declarar variáveis e parâmetros com tipo de função.
Exemplo
Considere o padrão de "empréstimo de recursos". Você deseja controlar a criação e a limpeza de um recurso, enquanto permite que o código do cliente "pegue emprestado" o recurso intermediário.
Isso declara um tipo de delegado.
Qualquer método que corresponda a essa assinatura pode ser usado para instanciar um delegado desse tipo. No C # 2.0, isso pode ser feito implicitamente, simplesmente usando o nome do método, bem como usando métodos anônimos.
Este método usa o tipo como parâmetro. Observe a invocação do delegado.
A função pode ser chamada com um método anônimo da seguinte maneira. Observe que o método anônimo pode usar variáveis declaradas fora de si mesmo. Isso é extremamente útil (embora o exemplo seja um pouco artificial).
fonte
Os delegados geralmente podem ser usados no lugar de uma interface com um método; um exemplo comum disso seria o padrão do observador. Em outros idiomas, se você deseja receber uma notificação de que algo aconteceu, você pode definir algo como:
Em C #, isso é mais comumente expresso por meio de eventos, em que o manipulador é um delegado, por exemplo:
Outro ótimo lugar para usar delegados se você precisar passar um predicado para uma função, por exemplo, ao selecionar um conjunto de itens de uma lista:
A descrição acima é um exemplo da sintaxe lambda, que também poderia ter sido escrita da seguinte maneira:
Outro lugar onde pode ser útil usar delegados é registrar funções de fábrica, por exemplo:
Eu espero que isso ajude!
fonte
Estou chegando nisso muito tarde, mas estava tendo problemas para descobrir o propósito dos delegados hoje e escrevi dois programas simples que fornecem a mesma saída que eu acho que explica bem seu propósito.
NoDelegates.cs
Delegates.cs
fonte
Um uso ligeiramente diferente é acelerar a reflexão; isto é, em vez de usar reflexão a cada vez, você pode usar
Delegate.CreateDelegate
para criar um delegado (digitado) para um método (aMethodInfo
) e chamar esse delegado. Isso é muito mais rápido por chamada, pois as verificações já foram feitas.Com o
Expression
, você também pode fazer o mesmo para criar código em tempo real - por exemplo, você pode criar facilmente umExpression
que representa o operador + para um tipo escolhido em tempo de execução (para fornecer suporte de operador para genéricos, que a linguagem não fornece) ; e você pode compilar umExpression
para um delegado digitado - trabalho feito.fonte
Os delegados são usados sempre que você usa eventos - esse é o mecanismo pelo qual eles funcionam.
Além disso, os delegados são muito úteis para coisas como o uso de consultas LINQ. Por exemplo, muitas consultas LINQ usam um delegado (frequentemente
Func<T,TResult>
) que pode ser usado para filtragem.fonte
inscrever manipuladores de eventos para eventos
fonte
Um exemplo pode ser visto aqui . Você tem um método para processar um objeto que atende a certos requisitos. No entanto, você deseja processar o objeto de várias maneiras. Em vez de criar métodos separados, você pode simplesmente atribuir um método de correspondência que processa o objeto a um delegado e passa o delegado ao método que seleciona os objetos. Dessa forma, você pode atribuir métodos diferentes para o método de um seletor. Tentei tornar isso facilmente compreensível.
fonte
Eu uso delegados para me comunicar com threads.
Por exemplo, posso ter um aplicativo win forms que baixa um arquivo. O aplicativo inicia um thread de trabalho para fazer o download (o que evita que a GUI seja travada). O thread de trabalho usa delegados para enviar mensagens de status (por exemplo, progresso do download) de volta ao programa principal, para que a GUI possa atualizar a barra de status.
fonte
Para manipulador de eventos
Para passar o método em parâmetros de um método
fonte
A primeira linha de uso é substituir o padrão Observer / Observable (eventos). A segunda, uma versão elegante e agradável do padrão Strategy. Vários outros usos podem ser reunidos, embora mais esotéricos do que esses dois primeiros, eu acho.
fonte
Eventos, outras operações anynch
fonte
Sempre que quiser encapsular o comportamento, mas invoque-o de maneira uniforme. Manipuladores de eventos, funções de retorno de chamada, etc. Você pode realizar coisas semelhantes usando interfaces e casts, mas às vezes, o comportamento não está necessariamente vinculado a um tipo ou objeto . Às vezes, você apenas tem um comportamento que precisa encapsular.
fonte
Inicialização de parâmetro lenta! Além de todas as respostas anteriores (padrão de estratégia, padrão de observador, etc.), os delegados permitem que você lide com a inicialização lenta de parâmetros. Por exemplo, suponha que você tenha uma função Download () que leva muito tempo e retorna um certo DownloadedObject. Este objeto é consumido por um Storage dependendo de certas Condições. Normalmente, você:
Porém, com delegados (mais precisamente, lambdas) você pode fazer o seguinte, alterando a assinatura da loja para que ela receba uma Condição e uma Func <Item, DownloadedObject> e use-a assim:
Portanto, o storage só avaliará o delegado se necessário, executando o download dependendo das condições.
fonte
Uso de delegados
fonte
O parâmetro de comparação em In Array.Sort (matriz T [], comparação de comparação), List.Sort (comparação de comparação), etc.
fonte
Pelo que eu sei, os delegados podem ser convertidos em ponteiros de função. Isso torna a vida MUITO mais fácil ao interoperar com código nativo que usa ponteiros de função, pois eles podem ser efetivamente orientados a objetos, mesmo que o programador original não tenha feito nenhuma provisão para que isso aconteça.
fonte
Delegate's são usados para chamar um método por sua referência. Por exemplo:
fonte