Quando você usaria delegados em C #? [fechadas]

101

Qual é o seu uso de delegados em C #?

Maxime Rouiller
fonte
2
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."?
Niki

Respostas:

100

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)
Jon Skeet
fonte
Vale a pena mencionar o "push" no Push LINQ?
Marc Gravell
3
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;

class Program
{
    static void Main()
    {
        List<String> names = new List<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);
    }

    static Boolean inMyFamily(String name)
    {
        return name.EndsWith("Hare");
    }
}
Andrew Hare
fonte
11
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.

Fonte: LosTechies

Exatamente como o LINQ está fazendo.

Maxime Rouiller
fonte
+ 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.

Isso declara um tipo de delegado.

public delegate void DataReaderUser( System.Data.IDataReader dataReader );

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.

public class DataProvider
{
    protected string _connectionString;

    public DataProvider( string psConnectionString )
    {
        _connectionString = psConnectionString;
    }

    public void UseReader( string psSELECT, DataReaderUser readerUser )
    {
        using ( SqlConnection connection = new SqlConnection( _connectionString ) )
        try
        {
            SqlCommand command = new SqlCommand( psSELECT, connection );
            connection.Open();
            SqlDataReader reader = command.ExecuteReader();

            while ( reader.Read() )
                readerUser( reader );  // the delegate is invoked
        }
        catch ( System.Exception ex )
        {
            // handle exception
            throw 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] );
    } );
arpão
fonte
11

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:

class IObserver{ void Notify(...); }

Em C #, isso é mais comumente expresso por meio de eventos, em que o manipulador é um delegado, por exemplo:

myObject.SomeEvent += delegate{ Console.WriteLine("..."); };

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:

myFactory.RegisterFactory(Widgets.Foo, () => new FooWidget());
var widget = myFactory.BuildWidget(Widgets.Foo);

Eu espero que isso ajude!

Jonnii
fonte
Bons exemplos com sintaxe .. Obrigado .. :)
Raghu
10

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;

public class Test {
    public const int MAX_VALUE = 255;
    public const int MIN_VALUE = 10;

    public static void 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");
        else
            Console.WriteLine("max and min value is not valid");
    }

    public static void checkMax(int a) {
        Console.Write("checkMax result of {0}: ", a);
        if (a < MAX_VALUE)
            Console.WriteLine("max value is valid");
        else
            Console.WriteLine("max value is not valid");
    }

    public static void checkMin(int a) {
        Console.Write("checkMin result of {0}: ", a);
        if (a > MIN_VALUE)
            Console.WriteLine("min value is valid");
        else
            Console.WriteLine("min value is not valid");
        Console.WriteLine("");
    }
}

public class Driver {
    public static void Main(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;

public delegate void Valid(int a);

public class Test {
    public const int MAX_VALUE = 255;
    public const int MIN_VALUE = 10;

    public static void 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");
        else
            Console.WriteLine("max and min value is not valid");
    }

    public static void checkMax(int a) {
        Console.Write("checkMax result of {0}: ", a);
        if (a < MAX_VALUE)
            Console.WriteLine("max value is valid");
        else
            Console.WriteLine("max value is not valid");
    }

    public static void checkMin(int a) {
        Console.Write("checkMin result of {0}: ", a);
        if (a > MIN_VALUE)
            Console.WriteLine("min value is valid");
        else
            Console.WriteLine("min value is not valid");
        Console.WriteLine("");
    }
}

public class Driver {
    public static void Main(string [] args) {
        Valid v1 = new Valid(Test.checkInt);
        v1 += new Valid(Test.checkMax);
        v1 += new Valid(Test.checkMin);
        v1(1);
        v1(10);
        v1(20);
        v1(30);
        v1(254);
        v1(255);
        v1(256);
    }
}
vai
fonte
5

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.

Marc Gravell
fonte
5

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.

Reed Copsey
fonte
4

inscrever manipuladores de eventos para eventos

Manu
fonte
2

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.

rookie1024
fonte
1

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.

Danny Frencham
fonte
0
  1. Para manipulador de eventos

  2. Para passar o método em parâmetros de um método

Patrick Desjardins
fonte
0

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.

x0n
fonte
0

Eventos, outras operações anynch


fonte
0

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.

Bob King
fonte
0

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.

Santiago Palladino
fonte
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.
Marc Gravell
0

Uso de delegados

  1. Manipulação de eventos
  2. Multi Casting
Rajeshwaran SP
fonte
0

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.

GregUzelac
fonte
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.

Cachorro
fonte
0

Delegate's são usados ​​para chamar um método por sua referência. Por exemplo:

  delegate void del_(int no1,int no2);
class Math
{
   public static void add(int x,int y)
   {
     Console.WriteLine(x+y);
   }
   public static void sub(int x,int y)
   {
     Console.WriteLine(x-y);
   }
}



    class Program
    {
        static void Main(string[] args)
        {
            del_ d1 = new del_(Math.add);
            d1(10, 20);
            del_ d2 = new del_(Math.sub);
            d2(20, 10);
            Console.ReadKey();
        }
    }
mahesh
fonte