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


101

Qual é o seu uso de delegados em C #?


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)

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");
    }
}

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.


+ 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] );
    } );

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!


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);
    }
}

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.


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.


4

inscrever manipuladores de eventos para eventos


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.


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.



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.


0

Eventos, outras operações anynch


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.


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.


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

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.


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.


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();
        }
    }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.