Diferença entre events e delegates e suas respectivas aplicações

Eu não vejo vantagens em usar events sobre delegates, além de ser um açúcar sintático. Talvez eu seja mal-entendido, mas parece que o evento é apenas um espaço reservado para o delegado.

Você me explicaria as diferenças e quando usar qual? Quais são as vantagens e desvantagens? Nosso código está fortemente enraizado em events, e eu quero chegar ao fundo disso.

Quando você usaria delegates sobre events e vice-versa? Por favor, indique sua experiência no mundo real com ambos, digamos, no código de produção.

   

Do ponto de vista técnico, outras respostas abordaram as diferenças.

De uma perspectiva semântica, events são ações levantadas por um object quando certas condições são atendidas. Por exemplo, minha class Stock tem uma propriedade chamada Limit e gera um evento quando os preços das ações atingem o limite. Esta notificação é feita por meio de um evento. Se alguém realmente se importa com esse evento e o subscreve, isso está além da preocupação da class proprietária.

Um delegado é um termo mais genérico para descrever uma construção semelhante a um ponteiro em termos C / C ++. Todos os delegates em .Net são delegates multicast. De uma perspectiva semântica, eles geralmente são usados ​​como um tipo de input. Em particular, eles são uma maneira perfeita de implementar o padrão de estratégia . Por exemplo, se eu quiser classificar uma lista de objects, posso fornecer uma estratégia de comparação ao método para informar à implementação como comparar dois objects.

Eu usei os dois methods no código de produção. Toneladas de meus objects de dados notificam quando certas propriedades são atendidas. Exemplo mais básico, sempre que uma propriedade é alterada, um evento PropertyChanged é gerado (consulte Interface INotifyPropertyChanged). Eu usei delegates em código para fornecer diferentes estratégias de transformar certos objects em string. Este exemplo em particular foi uma lista ToString () glorificada de implementações para um tipo de object específico para exibi-lo aos usuários.

O event palavra-chave é um modificador de escopo para delegates multicast. Diferenças práticas entre isso e apenas declarar um delegado multicast são as seguintes:

  • Você pode usar o event em uma interface.
  • O access de invocação ao delegado multicast é limitado à class declarante. O comportamento é como se o delegado fosse privado para invocação. Para fins de atribuição, o access é conforme especificado por um modificador de access explícito (por exemplo, public event ).

Por uma questão de interesse, você pode aplicar + e - para delegates multicast, e esta é a base da += e -= syntax para a atribuição de combinação de delegates para events. Esses três trechos são equivalentes:

 B = new EventHandler(this.MethodB); C = new EventHandler(this.MethodC); A = B + C; 

Amostra dois, ilustrando a atribuição direta e a atribuição de combinação.

 B = new EventHandler(this.MethodB); C = new EventHandler(this.MethodC); A = B; A += C; 

Amostra três: syntax mais familiar. Você provavelmente está familiarizado com a atribuição de null para remover todos os manipuladores.

 B = new EventHandler(this.MethodB); C = new EventHandler(this.MethodC); A = null; A += B; A += C; 

Como as propriedades, os events têm uma syntax completa que ninguém usa. Este:

 class myExample { internal EventHandler eh; public event EventHandler OnSubmit { add { eh = Delegate.Combine(eh, value) as EventHandler; } remove { eh = Delegate.Remove(eh, value) as EventHandler; } } ... } 

… faz exatamente o mesmo que isso:

 class myExample { public event EventHandler OnSubmit; } 

Os methods add e remove são mais visíveis na syntax bastante empolada que o VB.NET usa (sem sobrecarga de operadores).

Eventos são açúcar sintático. Eles são deliciosos. Quando vejo um evento, sei o que fazer. Quando vejo um delegado, não tenho tanta certeza.

Combinando events com interfaces (mais açúcar) contribui para um lanche de dar água na boca. Delegados e aulas abstratas virtuais puras são muito menos apetitosas.

Eventos são marcados como tal nos metadados. Isso permite que coisas como os designers do Windows Forms ou do ASP.NET distingam events de meras propriedades do tipo delegado e fornecem suporte apropriado para eles (especificamente exibindo-os na guia Eventos da janela Propriedades).

Outra diferença de uma propriedade do tipo delegado é que os usuários podem apenas adicionar e remover manipuladores de events, enquanto que com uma propriedade do tipo delegado eles podem definir o valor:

 someObj.SomeCallback = MyCallback; // okay, replaces any existing callback someObj.SomeEvent = MyHandler; // not okay, must use += instead 

Isso ajuda a isolar assinantes de events: posso adicionar meu manipulador a um evento e você pode adicionar seu manipulador ao mesmo evento e não replaceá acidentalmente meu manipulador.

Editar # 1 Quando você usaria delegates sobre events e vs.versa? Por favor, indique sua experiência no mundo real com ambos, digamos, no código de produção.

Quando eu desenho minhas próprias APIs, eu defino delegates que são passados ​​como parâmetros para methods, ou para os construtores de classs:

  • Para que um método possa implementar um padrão simples de “método de modelo” (como, por exemplo, os delegates de Predicate e Action são passados ​​para as classs de collections genéricas .Net)
  • Ou para que a class possa fazer um ‘retorno de chamada’ (normalmente um retorno de chamada para um método da class que o criou).

Esses representantes geralmente não são opcionais em tempo de execução (ou seja, não devem ser null ).

Eu não uso events; mas onde eu uso events, eu os uso para sinalizar events como zero, um ou mais clientes que poderiam estar interessados, ou seja, quando faz sentido que uma class (por exemplo, a class System.Windows.Form ) deva existir e ser executada ou nenhum cliente adicionou um manipulador de events ao seu evento (por exemplo, o evento ‘mouse para baixo’ do formulário existe, mas é opcional se algum cliente externo está interessado em instalar um manipulador de events nesse evento).

Embora os events sejam normalmente implementados com delegates multicast, não há exigência de que sejam usados ​​dessa maneira. Se uma class expuser evento, isso significa que a class expõe dois methods. Seus significados são, em essência:

  1. Aqui está um delegado. Por favor invoque quando algo interessante acontecer.
  2. Aqui está um delegado. Você deve destruir toda referência a ela assim que for conveniente (e não mais chamá-la).

A maneira mais comum para uma class manipular um evento que expõe é definir um delegado multicast e adicionar / remover qualquer delegado que seja passado para os methods acima, mas não há nenhum requisito de que eles funcionem dessa maneira. Infelizmente, a arquitetura de events falha em fazer algumas coisas que tornariam as abordagens alternativas muito mais limpas (por exemplo, ter o método de assinatura retornando um MethodInvoker, que seria mantido pelo assinante; cancelar a inscrição de um evento, simplesmente invocar o método retornado) para delegates multicast são de longe a abordagem mais comum.

para entender as diferenças, você pode olhar para este 2 exemplos

Exemplo com Delegados (Ação neste caso que é um tipo de delegado que não retorna valor)

 public class Animal { public Action Run {get; set;} public void RaiseEvent() { if (Run != null) { Run(); } } } 

para usar o delegado você deve fazer algo parecido com isto

 Animale animal= new Animal(); animal.Run += () => Console.WriteLine("I'm running"); animal.Run += () => Console.WriteLine("I'm still running") ; animal.RaiseEvent(); 

Esse código funciona bem, mas você pode ter alguns pontos fracos.

Por exemplo, se eu escrever isso

 animal.Run += () => Console.WriteLine("I'm running"); animal.Run += () => Console.WriteLine("I'm still running"); animal.Run = () => Console.WriteLine("I'm sleeping") ; 

com a última linha de código que eu tinha sobrescrever os comportamentos anteriores apenas com um faltando + (eu usei + invés de += )

Outro ponto fraco é que cada class que usa sua class Animal pode aumentar RaiseEvent apenas chamando de animal.RaiseEvent() .

Para evitar esses pontos fracos, você pode usar events em c #.

Sua class Animal vai mudar dessa maneira

 public class ArgsSpecial :EventArgs { public ArgsSpecial (string val) { Operation=val; } public string Operation {get; set;} } public class Animal { public event EventHandler Run = delegate{} //empty delegate. In this way you are sure that value is always != null because no one outside of the class can change it public void RaiseEvent() { Run(this, new ArgsSpecial("Run faster")); } } 

para chamar events

  Animale animal= new Animal(); animal.Run += (sender, e) => Console.WriteLine("I'm running. My value is {0}", e.Operation); animal.RaiseEvent(); 

Diferenças:

  1. Você não está usando uma propriedade pública, mas um campo público (com events, o compilador protege seus campos contra access indesejado)
  2. Eventos não podem ser atribuídos diretamente. Nesse caso, você não pode fazer o erro anterior que mostrei com a substituição do comportamento.
  3. Ninguém fora de sua class pode levantar o evento.
  4. Os events podem ser incluídos em uma declaração de interface, enquanto um campo não pode

notas

EventHandler é declarado como o seguinte delegado:

 public delegate void EventHandler (object sender, EventArgs e) 

é necessário um remetente (do tipo Object) e argumentos de evento. O remetente é nulo se vier de methods estáticos.

Você também pode usar EventHAndler neste exemplo que usa EventHandler

consulte aqui a documentação sobre EventHandler

Embora eu não tenha razões técnicas para isso, eu uso events no código de estilo da interface do usuário, em outras palavras, nos níveis mais altos do código, e uso delegates para a lógica que é mais profunda no código. Como eu disse, você poderia usar qualquer uma delas, mas acho que esse padrão de uso é logicamente correto, mas nada mais ajuda a documentar os tipos de callbacks e a hierarquia deles.


Edit: Eu acho que a diferença nos padrões de uso que tenho é que, eu acho perfeitamente aceitável ignorar events, eles são ganchos / stubs, se você precisa saber sobre o evento, ouvi-los, se você não se importa o evento apenas ignorá-lo. É por isso que eu os uso para interface do usuário, tipo estilo de evento JavaScript / Browser. No entanto, quando tenho um delegado, espero realmente que alguém manipule a tarefa do delegado e lance uma exceção se não for tratada.

A diferença entre os events e os delegates é muito menor do que eu pensava. Acabei de postar um vídeo super curto no YouTube sobre o assunto: https://www.youtube.com/watch?v=el-kKK-7SBU

Espero que isto ajude!

Se usarmos apenas delegar no lugar do Evento, o assinante terá a oportunidade de clonar (), invocar () o próprio delegado, como mostrado abaixo na imagem. O que não está certo.

insira a descrição da imagem aqui

Essa é a principal diferença entre o evento e o delegado. o assinante tem apenas um direito, ou seja, ouvir os events

Classe ConsoleLog está assinando events de log via EventLogHandler

 public class ConsoleLog { public ConsoleLog(Operation operation) { operation.EventLogHandler += print; } public void print(string str) { Console.WriteLine("write on console : " + str); } } 

A class FileLog está assinando events de log via EventLogHandler

 public class FileLog { public FileLog(Operation operation) { operation.EventLogHandler += print; } public void print(string str) { Console.WriteLine("write in File : " + str); } } 

A class de operação está publicando events de log

 public delegate void logDelegate(string str); public class Operation { public event logDelegate EventLogHandler; public Operation() { new FileLog(this); new ConsoleLog(this); } public void DoWork() { EventLogHandler.Invoke("somthing is working"); } }