quando e por que usar delegados? [duplicado]

[[2] esta pergunta já tem uma resposta aqui:

  • Onde Uso delegados? [fechado] 8 respostas

sou relativamente novo em C#, e estou a pensar quando usar os delegados apropriadamente. eles são amplamente utilizados na declaração de eventos, mas quando devo usá-los em meu próprio código e por que eles são úteis? Porque não usar outra coisa?

Também me pergunto quando terei de usar delegados e não tenho outra alternativa.

Obrigado pela ajuda!

EDIT: acho que encontrei um Uso necessário de delegados toma.

Author: iChaib, 2010-01-07

8 answers

Concordo com tudo o que já foi dito, só estou a tentar pôr outras palavras.

Um delegado pode ser visto como um substituto para a/some method(s).

Definindo um delegado, você está dizendo para o usuário de sua classe, "por Favor, sinta-se livre para atribuir, qualquer método que corresponde a esta assinatura, para o delegado e ele será chamado cada vez que o delegado é chamado".

O uso típico é, naturalmente, eventos. Todo o OnEventX delega nos métodos o utilizador define.

Os delegados são úteis para oferecer ao utilizador dos seus objectos alguma capacidade de personalizar o seu comportamento. Na maioria das vezes, você pode usar outras maneiras para alcançar o mesmo propósito e eu não acredito que você possa ser forçado a criar delegados. É apenas a maneira mais fácil em algumas situações para fazer a coisa feita.

 232
Author: Benoit Vidis, 2016-08-18 20:18:34

Um delegado é uma referência a um método. Enquanto objetos podem ser facilmente enviados como parâmetros em métodos, construtores ou o que quer que seja, os métodos são um pouco mais complicados. Mas de vez em quando você pode sentir a necessidade de enviar um método como um parâmetro para outro método, e é quando você vai precisar de delegados.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MyLibrary;

namespace DelegateApp {

  /// <summary>
  /// A class to define a person
  /// </summary>
  public class Person {
    public string Name { get; set; }
    public int Age { get; set; }
  }

  class Program {
    //Our delegate
    public delegate bool FilterDelegate(Person p);

    static void Main(string[] args) {

      //Create 4 Person objects
      Person p1 = new Person() { Name = "John", Age = 41 };
      Person p2 = new Person() { Name = "Jane", Age = 69 };
      Person p3 = new Person() { Name = "Jake", Age = 12 };
      Person p4 = new Person() { Name = "Jessie", Age = 25 };

      //Create a list of Person objects and fill it
      List<Person> people = new List<Person>() { p1, p2, p3, p4 };

      //Invoke DisplayPeople using appropriate delegate
      DisplayPeople("Children:", people, IsChild);
      DisplayPeople("Adults:", people, IsAdult);
      DisplayPeople("Seniors:", people, IsSenior);

      Console.Read();
    }

    /// <summary>
    /// A method to filter out the people you need
    /// </summary>
    /// <param name="people">A list of people</param>
    /// <param name="filter">A filter</param>
    /// <returns>A filtered list</returns>
    static void DisplayPeople(string title, List<Person> people, FilterDelegate filter) {
      Console.WriteLine(title);

      foreach (Person p in people) {
        if (filter(p)) {
          Console.WriteLine("{0}, {1} years old", p.Name, p.Age);
        }
      }

      Console.Write("\n\n");
    }

    //==========FILTERS===================
    static bool IsChild(Person p) {
      return p.Age < 18;
    }

    static bool IsAdult(Person p) {
      return p.Age >= 18;
    }

    static bool IsSenior(Person p) {
      return p.Age >= 65;
    }
  }
}
 256
Author: dhaval8087, 2018-01-04 05:46:38

Diz que queres escrever um procedimento para integrar alguma função real f (x ) em algum intervalo [a, b]. Digamos que queremos usar o método Gaussiano de 3 pontos para fazer isso (qualquer um fará, é claro).

Idealmente queremos uma função que se pareça com:

// 'f' is the integrand we want to integrate over [a, b] with 'n' subintervals.
static double Gauss3(Integrand f, double a, double b, int n) {
  double res = 0;

  // compute result
  // ...

  return res;
}
Então podemos passar em qualquer lugar.Integrand, f , e obter a sua integral definitiva ao longo do intervalo fechado.

Que tipo deveria ser Integrand?

Sem Delegados Bem, sem delegados, precisaríamos de algum tipo de interface com um único método, digamos eval declarado do seguinte modo:
// Interface describing real-valued functions of one variable.
interface Integrand {
  double eval(double x);
}
Então precisamos criar um monte de classes implementando esta interface, do seguinte modo:
// Some function
class MyFunc1 : Integrand {
  public double eval(double x) {
    return /* some_result */ ;
  }
}

// Some other function
class MyFunc2 : Integrand {
  public double eval(double x) {
    return /* some_result */ ;
  }
}

// etc

Então para usá-los no nosso método Gauss3, precisamos invocá-los da seguinte forma:

double res1 = Gauss3(new MyFunc1(), -1, 1, 16);
double res2 = Gauss3(new MyFunc2(), 0, Math.PI, 16);

E o Gauss3 tem de fazer o seguinte:

static double Gauss3(Integrand f, double a, double b, int n) {
  // Use the integrand passed in:
  f.eval(x);
}
Por isso, precisamos de fazer tudo isso só para usar as nossas funções arbitrárias em Guass3. Com Delegados
public delegate double Integrand(double x);
Agora podemos definir algumas funções estáticas (ou não) aderindo a esse protótipo:
class Program {
   public delegate double Integrand(double x);   
   // Define implementations to above delegate 
   // with similar input and output types
   static double MyFunc1(double x) { /* ... */ }
   static double MyFunc2(double x) { /* ... */ }
   // ... etc ...

   public static double Gauss3(Integrand f, ...) { 
      // Now just call the function naturally, no f.eval() stuff.
      double a = f(x); 
      // ...
   }

   // Let's use it
   static void Main() {
     // Just pass the function in naturally (well, its reference).
     double res = Gauss3(MyFunc1, a, b, n);
     double res = Gauss3(MyFunc2, a, b, n);    
   }
}
Nada de interfaces, nada de palhaçadas .material eval, sem instanciação de objeto, apenas um simples ponteiro de função como o uso, para uma tarefa simples. É claro que os delegados são mais do que apenas apontadores de função sob o capô, mas isso é uma questão separada (encadeamento de funções e eventos).
 138
Author: Alex Budovski, 2016-09-15 02:04:50
Os delegados são extremamente úteis quando querem declarar um bloco de código que querem passar. Por exemplo, ao usar um mecanismo genérico de repetição.

Pseudo:

function Retry(Delegate func, int numberOfTimes)
    try
    {
       func.Invoke();
    }
    catch { if(numberOfTimes blabla) func.Invoke(); etc. etc. }

Ou quando você quer fazer a avaliação tardia de blocos de código, como uma função onde você tem alguns Transform ação, e quer ter um BeforeTransform e AfterTransform ação que você pode avaliar, dentro de sua função de Transformação, sem ter para saber se a BeginTransform é preenchido, ou o que ele tem para transformar.

E, claro, ao criar manipuladores de eventos. Você não quer avaliar o código agora, mas apenas quando necessário, então você registra um delegado que pode ser invocado quando o evento ocorrer.
 26
Author: Jan Jongboom, 2010-01-07 09:56:42

Resumo Dos Delegados

Os delegados têm as seguintes propriedades:

  • Os delegados são semelhantes aos ponteiros da função C++, mas são seguros do tipo.
  • Os delegados permitem que os métodos sejam passados como parâmetros. Os delegados podem ser usados para definir métodos de callback.
  • Os delegados podem ser acorrentados em conjunto; por exemplo, vários métodos podem ser chamados num único evento.
  • Os métodos não precisam de corresponder à assinatura do delegado exactamente. Para mais informações, consulte covariância e contra variância.
  • C# Versão 2.0 introduz o conceito de métodos anônimos, que permitem que blocos de código sejam passados como parâmetros em lugar de um método definido separadamente.
 21
Author: Lukas Šalkauskas, 2016-12-13 00:06:15

Acabei de dar a volta a estas ideias, e por isso vou partilhar um exemplo como já tem Descrições, mas no momento uma vantagem que vejo é contornar os avisos circulares de estilo de referência onde não se podem ter 2 projectos a referirem-se uns aos outros.

Vamos assumir que uma aplicação transfere um XML, e depois grava o XML para uma base de dados. Tenho aqui 2 projectos que constroem a minha solução: FTP e SaveDatabase. Então, a nossa candidatura começa por procurar qualquer descarrega e descarrega o(s) ficheiro (s), em seguida, chama o projecto SaveDatabase.

Agora, a nossa aplicação precisa notificar o site FTP quando um arquivo é salvo para a base de dados, enviando um arquivo com Meta-dados (ignorar por que, é um pedido do proprietário do site FTP). A questão está em que ponto e como? Precisamos de um novo método chamado Notifyftplete (), mas em qual dos nossos projetos deve ser salvo também - FTP ou SaveDatabase? Logicamente, o código deve viver em nosso projeto FTP. Mas isto ... significaria que a nossa Notifyftplete terá de ser despoletada ou, terá de esperar até que a gravação esteja completa, e, em seguida, consultar a base de dados para garantir que ela está lá. O que precisamos fazer é dizer ao nosso projeto SaveDatabase para chamar o método Notiftplete() direto, mas não podemos; teríamos uma referência ciruclar e o Notifyftplete() é um método privado. Que pena, isto teria resultado. Bem, pode.

Durante o código da nossa aplicação, teríamos passado parâmetros entre se um desses parâmetros fosse o método de notificação completa. Sim, passamos o método, com todo o código dentro também. Isso significaria que poderíamos executar o método em qualquer ponto, a partir de qualquer projeto. Bem, isto é o que o delegado é. Isto significa que podemos passar o método Notiftplete() como um parâmetro para a nossa classe SaveDatabase (). No ponto que salva, simplesmente executa o delegado.

Veja se este exemplo bruto ajuda (pseudo-código). Também assumiremos que o a aplicação começa com o método Begin() da classe FTP.

class FTP
{
    public void Begin()
    {
        string filePath = DownloadFileFromFtpAndReturnPathName();

        SaveDatabase sd = new SaveDatabase();
        sd.Begin(filePath, NotifyFtpComplete());
    }

    private void NotifyFtpComplete()
    {
        //Code to send file to FTP site
    }
}


class SaveDatabase
{
    private void Begin(string filePath, delegateType NotifyJobComplete())
    {
        SaveToTheDatabase(filePath);

        //InvokeTheDelegate - here we can execute the NotifyJobComplete method at our preferred moment in the application, despite the method being private and belonging to a different class. 
        NotifyJobComplete.Invoke();
    }
}

Então, com isso explicado, podemos fazê-lo de verdade Agora com esta aplicação de consola usando C#

using System;

namespace ConsoleApplication1
{
    //I've made this class private to demonstrate that the SaveToDatabase cannot have any knowledge of this Program class.
    class Program
    {
        static void Main(string[] args)
        {
            //Note, this NotifyDelegate type is defined in the SaveToDatabase project
            NotifyDelegate nofityDelegate = new NotifyDelegate(NotifyIfComplete);

            SaveToDatabase sd = new SaveToDatabase();            
            sd.Start(nofityDelegate);
            Console.ReadKey();
        }

        //this is the method which will be delegated - the only thing it has in common with the NofityDelegate is that it takes 0 parameters and that it returns void. However, it is these 2 which are essential. It is really important to notice that it writes a variable which, due to no constructor, has not yet been called (so _notice is not initialized yet). 
    private static void NotifyIfComplete()
    {
        Console.WriteLine(_notice);
    }

    private static string _notice = "Notified";
    }


    public class SaveToDatabase
    {
        public void Start(NotifyDelegate nd)
        {
            Console.WriteLine("Yes, I shouldn't write to the console from here, it's just to demonstrate the code executed.");
            Console.WriteLine("SaveToDatabase Complete");
            Console.WriteLine(" ");
            nd.Invoke();
        }
    }
    public delegate void NotifyDelegate();
}

Eu sugiro que você atravesse o código e veja quando _notice é chamado e quando o método (delegado) é chamado como este, eu espero, vai deixar as coisas muito claras.

No entanto, por fim, podemos torná-lo mais útil alterando o tipo de delegado para incluir um parâmetro.
using System.Text;

namespace ConsoleApplication1
{
    //I've made this class private to demonstrate that the SaveToDatabase cannot have any knowledge of this Program class.
    class Program
    {
        static void Main(string[] args)
        {
            SaveToDatabase sd = new SaveToDatabase();

//Please note, that although NotifyIfComplete() takes a string parameter, we do not declare it - all we want to do is tell C# where the method is so it can be referenced later - we will pass the paramater later.
            NotifyDelegateWithMessage nofityDelegateWithMessage = new NotifyDelegateWithMessage(NotifyIfComplete);

            sd.Start(nofityDelegateWithMessage);

            Console.ReadKey();
        }

        private static void NotifyIfComplete(string message)
        {
            Console.WriteLine(message);
        }
    }


    public class SaveToDatabase
    {
        public void Start(NotifyDelegateWithMessage nd)
        {
            //To simulate a saving fail or success, I'm just going to check the current time (well, the seconds) and store the value as variable.
            string message = string.Empty;
            if (DateTime.Now.Second > 30)
                message = "Saved";
            else
                message = "Failed";

            //It is at this point we pass the parameter to our method.
            nd.Invoke(message);
        }
    }

    public delegate void NotifyDelegateWithMessage(string message);
}
 20
Author: Dave Rook, 2013-02-22 08:23:19

Um delegado é uma classe simples que é usada para apontar métodos com uma assinatura específica, tornando-se essencialmente um ponteiro de função tipo-seguro. O objetivo de um delegado é facilitar uma chamada de volta a outro método (ou métodos), depois de uma ter sido concluída, de uma forma estruturada.

Embora possa ser possível criar um extenso conjunto de código para executar esta funcionalidade, você não precisa também. Podes usar um delegado.

Criar um delegado é fácil de fazer. Identificar a classe como delegado com a palavra-chave "delegado". Em seguida, especifique a assinatura do tipo.
 3
Author: Pankaj, 2010-01-07 12:51:00