Uma declaração de evento adiciona uma camada de abstração e proteção à instância delegada . Essa proteção impede que os clientes do delegado redefinam o delegado e sua lista de chamadas e apenas permite adicionar ou remover destinos da lista de chamadas.
Obviamente, essa camada de proteção também impede que "clientes" (código fora da classe / estrutura de definição) invoquem o delegado e obtenham de qualquer forma o objeto delegado "por trás" do evento.
Jeppe Stig Nielsen
7
Não é inteiramente verdade. Você pode declarar um evento sem uma instância de delegação de back-end. No c #, você pode implementar um evento explicitamente e usar uma estrutura de dados de back-end diferente de sua escolha.
Miguel Gamboa
3
@mmcdole você pode dar um exemplo para explicar a dele?
precisa saber é o seguinte
103
Para entender as diferenças, você pode ver estes 2 exemplos
Exemplo com delegados (neste caso, uma ação - é um tipo de delegado que não retorna um valor)
Animal animal=newAnimal();
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, substituí os comportamentos anteriores apenas com um ausente +(usei em =vez de +=)
Outro ponto fraco é que toda classe que usa sua Animalclasse pode aumentar RaiseEventapenas chamando animal.RaiseEvent().
Para evitar esses pontos fracos, você pode usar eventsem c #.
Sua classe Animal será alterada desta maneira:
publicclassArgsSpecial:EventArgs{publicArgsSpecial(string val){Operation=val;}publicstringOperation{get;set;}}publicclassAnimal{// Empty delegate. In this way you are sure that value is always != null // because no one outside of the class can change it.publiceventEventHandler<ArgsSpecial>Run=delegate{}publicvoidRaiseEvent(){Run(this,newArgsSpecial("Run faster"));}}
chamar eventos
Animal animal=newAnimal();
animal.Run+=(sender, e)=>Console.WriteLine("I'm running. My value is {0}", e.Operation);
animal.RaiseEvent();
Diferenças:
Você não está usando uma propriedade pública, mas um campo público (usando eventos, o compilador protege seus campos de acesso indesejado)
Eventos não podem ser atribuídos diretamente. Nesse caso, não dará origem ao erro anterior que mostrei ao substituir o comportamento.
Ninguém fora da sua turma pode promover o evento.
Eventos podem ser incluídos em uma declaração de interface, enquanto um campo não pode
Notas:
EventHandler é declarado como o seguinte delegado:
Tudo parecia ótimo até eu me deparar com "Ninguém fora da sua turma pode promover o evento". O que isso significa? Ninguém pode ligar RaiseEventdesde que um método de chamada tenha acesso a uma instância do animalcódigo que usa o evento?
dance2die
11
Os eventos @Sung só podem ser gerados de dentro da classe, talvez eu não tenha sido claro explicando isso. Com eventos, você pode chamar a função que gera o evento (encapsulamento), mas ela só pode ser gerada de dentro da classe que o define. Deixe-me saber se eu não estiver claro.
@ baby, você quer dizer que, embora o evento seja declarado público, eu ainda não posso fazer isso animal.Run(this, new ArgsSpecial("Run faster");?
18816 Pap
1
@ChieltenBrinke É claro que o evento pode ser atribuído a membros da classe ... mas não o contrário.
Jim Balter
94
Além das propriedades sintáticas e operacionais, há também uma diferença semântica.
Delegados são, conceitualmente, modelos de função; isto é, eles expressam um contrato ao qual uma função deve aderir para serem considerados do "tipo" do delegado.
Eventos representam ... bem, eventos. Eles pretendem alertar alguém quando algo acontece e, sim, eles seguem uma definição de delegado, mas não são a mesma coisa.
Mesmo se fossem exatamente a mesma coisa (sintaticamente e no código IL), ainda restará a diferença semântica. Em geral, prefiro ter dois nomes diferentes para dois conceitos diferentes, mesmo que sejam implementados da mesma maneira (o que não significa que eu gostaria de ter o mesmo código duas vezes).
Então, poderíamos dizer que um evento é um tipo "especial" de delegado?
Pap
Não entendi seu ponto. Você pode usar um delegado para 'alertar alguém quando algo acontecer'. Talvez você não faça isso, mas pode e, portanto, não é uma propriedade inerente do evento.
steve
@Jorge Córdoba exemplo de delegado e delegado de eventos é o proprietário do jornal e os eventos (Assinar ou cancelar a inscrição) e algumas pessoas compram o jornal e outras não, o que significa que o proprietário do jornal não pode forçar todas as pessoas a comprarem o jornal. certo ou errado?
Resumidamente, a retirada do artigo - Eventos são encapsulamentos sobre delegados.
Citação do artigo:
Suponha que eventos não existissem como um conceito em C # / .NET. Como outra classe se inscreveria em um evento? Três opções:
Uma variável pública delegada
Uma variável delegada apoiada por uma propriedade
Uma variável delegada com os métodos AddXXXHandler e RemoveXXXHandler
A opção 1 é claramente horrível, por todas as razões normais que abominamos variáveis públicas.
A opção 2 é um pouco melhor, mas permite que os assinantes se substituam efetivamente - seria muito fácil escrever someInstance.MyEvent = eventHandler; que substituiria qualquer manipulador de eventos existente em vez de adicionar um novo. Além disso, você ainda precisa escrever as propriedades.
A opção 3 é basicamente o que os eventos fornecem, mas com uma convenção garantida (gerada pelo compilador e apoiada por sinalizadores extras na IL) e uma implementação "gratuita" se você estiver satisfeito com a semântica que os eventos semelhantes a campos fornecem. A inscrição e a desinscrição de eventos são encapsuladas sem permitir acesso arbitrário à lista de manipuladores de eventos, e os idiomas podem simplificar as coisas, fornecendo sintaxe para a declaração e a assinatura.
Isso é mais uma preocupação teórica do que qualquer coisa, mas FWIW sempre achei que o argumento "A opção 1 é ruim porque não gostamos de variáveis públicas" poderia usar um pouco mais de esclarecimento. Se ele está dizendo isso porque é "má prática de POO", tecnicamente uma public Delegatevariável estaria expondo "dados", mas, pelo que sei, a POO nunca mencionou conceitos como a Delegate(não é um "objeto" nem uma "mensagem") e o .NET mal trata os delegados como dados de qualquer maneira.
JRH
Embora eu também gostaria de dar conselhos mais práticos, se você estiver em uma situação em que deseja garantir que haja apenas um manipulador, criar seus próprios AddXXXHandlermétodos com uma private Delegatevariável pode ser uma boa opção. Nesse caso, você pode verificar se um manipulador já está definido e reagir adequadamente. Essa também pode ser uma boa configuração, se você precisar que o objeto que está segurando Delegateseja capaz de limpar todos os manipuladores ( eventnão fornece nenhuma maneira de fazer isso).
Jrh 29/08/19
7
NOTA: Se você tiver acesso ao C # 5.0 Unleashed , leia as "Limitações no uso simples de delegados" no capítulo 18, intitulado "Eventos" para entender melhor as diferenças entre os dois.
Sempre me ajuda a ter um exemplo simples e concreto. Então aqui está um para a comunidade. Primeiro, mostro como você pode usar delegados sozinhos para fazer o que os Eventos fazem por nós. Então eu mostro como a mesma solução funcionaria com uma instância de EventHandler. E então eu explico por que não queremos fazer o que explico no primeiro exemplo. Este post foi inspirado em um artigo de John Skeet.
Exemplo 1: Usando delegado público
Suponha que eu tenha um aplicativo WinForms com uma única caixa suspensa. O menu suspenso está vinculado a um List<Person>. Onde Person tem propriedades de Id, Name, NickName, HairColor. No formulário principal, há um controle de usuário personalizado que mostra as propriedades dessa pessoa. Quando alguém seleciona uma pessoa no menu suspenso, os rótulos na atualização de controle do usuário mostram as propriedades da pessoa selecionada.
Aqui está como isso funciona. Temos três arquivos que nos ajudam a montar isso:
Mediator.cs - classe estática mantém os delegados
Form1.cs - formulário principal
DetailView.cs - o controle do usuário mostra todos os detalhes
Aqui está o código relevante para cada uma das classes:
classMediator{publicdelegatevoidPersonChangedDelegate(Person p);//delegate type definitionpublicstaticPersonChangedDelegatePersonChangedDel;//delegate instance. Detail view will "subscribe" to this.publicstaticvoidOnPersonChanged(Person p)//Form1 will call this when the drop-down changes.{if(PersonChangedDel!=null){PersonChangedDel(p);}}}
Finalmente, temos o seguinte código em nosso Form1.cs. Aqui estamos chamando OnPersonChanged, que chama qualquer código inscrito no delegado.
privatevoid comboBox1_SelectedIndexChanged(object sender,EventArgs e){Mediator.OnPersonChanged((Person)comboBox1.SelectedItem);//Call the mediator's OnPersonChanged method. This will in turn call all the methods assigned (i.e. subscribed to) to the delegate -- in this case `DetailView_PersonChanged`.}
Está bem. Então é assim que você faria isso funcionar sem usar eventos e apenas usando delegados . Acabamos de colocar um representante público em uma classe - você pode torná-lo estático ou único, ou o que for. Ótimo.
MAS, MAS, MAS, nós não queremos fazer o que acabei de descrever acima. Porque os campos públicos são ruins por muitos, muitos motivos. Então, quais são nossas opções? Como John Skeet descreve, aqui estão nossas opções:
Uma variável pública delegada (foi o que acabamos de fazer acima. Não faça isso. Acabei de lhe dizer por que é ruim)
Coloque o delegado em uma propriedade com um get / set (o problema aqui é que os assinantes podem se substituir - para que possamos assinar vários métodos para o delegado e, em seguida, acidentalmente digamos PersonChangedDel = null, eliminando todas as outras assinaturas. outro problema que permanece aqui é que, como os usuários têm acesso ao delegado, eles podem invocar os destinos na lista de chamadas - não queremos que usuários externos tenham acesso a quando criar nossos eventos.
Uma variável delegada com os métodos AddXXXHandler e RemoveXXXHandler
Esta terceira opção é essencialmente o que um evento nos oferece. Quando declaramos um EventHandler, ele nos dá acesso a um delegado - não publicamente, não como uma propriedade, mas como isso chamamos de um evento que acabou de adicionar / remover acessadores.
Vamos ver como é o mesmo programa, mas agora usando um Evento em vez do representante público (eu também mudei nosso Mediador para um singleton):
Exemplo 2: Com EventHandler em vez de um representante público
Mediador:
classMediator{privatestaticreadonlyMediator_Instance=newMediator();privateMediator(){}publicstaticMediatorGetInstance(){return_Instance;}publiceventEventHandler<PersonChangedEventArgs>PersonChanged;//this is just a property we expose to add items to the delegate.publicvoidOnPersonChanged(object sender,Person p){var personChangedDelegate =PersonChangedasEventHandler<PersonChangedEventArgs>;if(personChangedDelegate !=null){
personChangedDelegate(sender,newPersonChangedEventArgs(){Person= p });}}}
Observe que se você F12 no EventHandler, ele mostrará que a definição é apenas um delegado genérico com o objeto "remetente" extra:
Embora apreciei todo o bom trabalho deste post e gostei de ler a maioria, ainda sinto que um problema não foi resolvido - The other problem that remains here is that since the users have access to the delegate, they can invoke the targets in the invocation list -- we don't want external users having access to when to raise our events. Na versão mais recente do Mediator, você ainda pode chamar o OnPersonChangesempre que tiver uma referência ao singleton. Talvez você deva mencionar que a Mediatorabordagem não impede esse comportamento específico e está mais próxima de um barramento de eventos.
Ivaylo Slavov
6
Você também pode usar eventos em declarações de interface, não para delegados.
A interface do @surfen pode conter eventos, mas não delegados.
Alexandr Nikitin
1
O que exatamente você quer dizer? Você pode ter Action a { get; set; }dentro de uma definição de interface.
Chiel ten Brinke
6
Que grande mal-entendido entre eventos e delegados !!! Um delegado especifica um TYPE (como um class, ou interfacefaz), enquanto um evento é apenas um tipo de MEMBER (como campos, propriedades, etc.). E, como qualquer outro tipo de membro, um evento também tem um tipo. No entanto, no caso de um evento, o tipo do evento deve ser especificado por um delegado. Por exemplo, você NÃO PODE declarar um evento de um tipo definido por uma interface.
Concluindo, podemos fazer a seguinte observação: o tipo de um evento DEVE ser definido por um delegado . Esta é a principal relação entre um evento e um delegado e é descrita na seção II.18. Definindo eventos das partições I a VI do ECMA-335 (CLI) :
No uso típico, o TypeSpec (se presente) identifica um delegado cuja assinatura corresponde aos argumentos transmitidos ao método de disparo do evento.
No entanto, esse fato NÃO implica que um evento use um campo de delegado de apoio . Na verdade, um evento pode usar um campo de apoio de qualquer tipo diferente de estrutura de dados de sua escolha. Se você implementar um evento explicitamente em C #, poderá escolher a maneira como armazena os manipuladores de eventos (observe que os manipuladores de eventos são instâncias do tipo do evento , que por sua vez é obrigatoriamente um tipo de delegado --- da Observação anterior ) Mas, você pode armazenar os manipuladores de eventos (que são instâncias de delegação) em uma estrutura de dados como uma Listou outra Dictionaryou qualquer outra coisa, ou mesmo em um campo de delegado de apoio. Mas não esqueça que NÃO é obrigatório que você use um campo de delegado.
Um evento no .net é uma combinação designada de um método Add e um método Remove, os quais esperam algum tipo específico de delegado. O C # e o vb.net podem gerar automaticamente código para os métodos add e remove, que definirão um delegado para armazenar as inscrições de eventos e adicionar / remover o delegado passado no / para o delegado da assinatura. O VB.net também gerará automaticamente o código (com a instrução RaiseEvent) para invocar a lista de assinaturas se e somente se não estiver vazia; por alguma razão, o C # não gera o último.
Observe que, embora seja comum gerenciar assinaturas de eventos usando um representante multicast, esse não é o único meio de fazer isso. De uma perspectiva pública, um possível assinante de evento precisa saber como informar um objeto que deseja receber eventos, mas não precisa saber qual mecanismo o editor usará para gerar os eventos. Observe também que, enquanto quem definiu a estrutura de dados do evento em .net aparentemente pensou que deveria haver um meio público de aumentá-los, nem o C # nem o vb.net fazem uso desse recurso.
Evento é uma REFERÊNCIA a um delegado com duas restrições
Não pode ser invocado diretamente
Não é possível atribuir valores diretamente (por exemplo, eventObj = delegateMethod)
Acima de dois, estão os pontos fracos dos delegados e são abordados no evento. Exemplo de código completo para mostrar a diferença no violinista está aqui https://dotnetfiddle.net/5iR3fB .
Alterne o comentário entre Evento e Delegado e o código do cliente que chama / atribui valores para delegar para entender a diferença
Aqui está o código embutido.
/*
This is working program in Visual Studio. It is not running in fiddler because of infinite loop in code.
This code demonstrates the difference between event and delegate
Event is an delegate reference with two restrictions for increased protection
1. Cannot be invoked directly
2. Cannot assign value to delegate reference directly
Toggle between Event vs Delegate in the code by commenting/un commenting the relevant lines
*/publicclassRoomTemperatureController{privateint _roomTemperature =25;//Default/Starting room Temperatureprivatebool _isAirConditionTurnedOn =false;//Default AC is Offprivatebool _isHeatTurnedOn =false;//Default Heat is Offprivatebool _tempSimulator =false;publicdelegatevoidOnRoomTemperatureChange(int roomTemperature);//OnRoomTemperatureChange is a type of Delegate (Check next line for proof)// public OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above), publiceventOnRoomTemperatureChangeWhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above), publicRoomTemperatureController(){WhenRoomTemperatureChange+=InternalRoomTemperatuerHandler;}privatevoidInternalRoomTemperatuerHandler(int roomTemp){System.Console.WriteLine("Internal Room Temperature Handler - Mandatory to handle/ Should not be removed by external consumer of ths class: Note, if it is delegate this can be removed, if event cannot be removed");}//User cannot directly asign values to delegate (e.g. roomTempControllerObj.OnRoomTemperatureChange = delegateMethod (System will throw error)publicboolTurnRoomTeperatureSimulator{set{
_tempSimulator =value;if(value){SimulateRoomTemperature();//Turn on Simulator }}get{return _tempSimulator;}}publicvoidTurnAirCondition(bool val){
_isAirConditionTurnedOn = val;
_isHeatTurnedOn =!val;//Binary switch If Heat is ON - AC will turned off automatically (binary)System.Console.WriteLine("Aircondition :"+ _isAirConditionTurnedOn);System.Console.WriteLine("Heat :"+ _isHeatTurnedOn);}publicvoidTurnHeat(bool val){
_isHeatTurnedOn = val;
_isAirConditionTurnedOn =!val;//Binary switch If Heat is ON - AC will turned off automatically (binary)System.Console.WriteLine("Aircondition :"+ _isAirConditionTurnedOn);System.Console.WriteLine("Heat :"+ _isHeatTurnedOn);}publicasyncvoidSimulateRoomTemperature(){while(_tempSimulator){if(_isAirConditionTurnedOn)
_roomTemperature--;//Decrease Room Temperature if AC is turned Onif(_isHeatTurnedOn)
_roomTemperature++;//Decrease Room Temperature if AC is turned OnSystem.Console.WriteLine("Temperature :"+ _roomTemperature);if(WhenRoomTemperatureChange!=null)WhenRoomTemperatureChange(_roomTemperature);System.Threading.Thread.Sleep(500);//Every second Temperature changes based on AC/Heat Status}}}publicclassMySweetHome{RoomTemperatureController roomController =null;publicMySweetHome(){
roomController =newRoomTemperatureController();
roomController.WhenRoomTemperatureChange+=TurnHeatOrACBasedOnTemp;//roomController.WhenRoomTemperatureChange = null; //Setting NULL to delegate reference is possible where as for Event it is not possible.//roomController.WhenRoomTemperatureChange.DynamicInvoke();//Dynamic Invoke is possible for Delgate and not possible with Event
roomController.SimulateRoomTemperature();System.Threading.Thread.Sleep(5000);
roomController.TurnAirCondition(true);
roomController.TurnRoomTeperatureSimulator=true;}publicvoidTurnHeatOrACBasedOnTemp(int temp){if(temp >=30)
roomController.TurnAirCondition(true);if(temp <=15)
roomController.TurnHeat(true);}publicstaticvoidMain(string[]args){MySweetHome home =newMySweetHome();}}
Se você marcar Intermediate Language, você saberá que o compilador .net converte delegate para uma classe selada na IL com algumas funções internas, como invoke, beginInvoke, endInvoke e delegate class herdadas de outra classe, talvez chamada "SystemMulticast". Eu acho que Event é uma classe filho de Delegado com algumas propriedades adicionais.
A diferença entre instância do evento e delegado é que você não pode executar o evento fora da declaração. Se você declarar um evento na classe A, poderá executá-lo apenas na classe A. Se você declarar um delegado na Classe A, poderá usá-lo em qualquer lugar. Eu acho que essa é a principal diferença entre eles
Respostas:
Uma declaração de evento adiciona uma camada de abstração e proteção à instância delegada . Essa proteção impede que os clientes do delegado redefinam o delegado e sua lista de chamadas e apenas permite adicionar ou remover destinos da lista de chamadas.
fonte
Para entender as diferenças, você pode ver estes 2 exemplos
Exemplo com delegados (neste caso, uma ação - é um tipo de delegado que não retorna um valor)
Para usar o delegado, você deve fazer algo assim:
Esse código funciona bem, mas você pode ter alguns pontos fracos.
Por exemplo, se eu escrever isso:
com a última linha de código, substituí os comportamentos anteriores apenas com um ausente
+
(usei em=
vez de+=
)Outro ponto fraco é que toda classe que usa sua
Animal
classe pode aumentarRaiseEvent
apenas chamandoanimal.RaiseEvent()
.Para evitar esses pontos fracos, você pode usar
events
em c #.Sua classe Animal será alterada desta maneira:
chamar eventos
Diferenças:
Notas:
EventHandler é declarado como o seguinte delegado:
leva um remetente (do tipo Object) e argumentos de evento. O remetente é nulo se for proveniente de métodos estáticos.
Este exemplo, que usa
EventHandler<ArgsSpecial>
, também pode ser escrito usandoEventHandler
.Consulte aqui para documentação sobre EventHandler
fonte
RaiseEvent
desde que um método de chamada tenha acesso a uma instância doanimal
código que usa o evento?animal.Run(this, new ArgsSpecial("Run faster");
?Além das propriedades sintáticas e operacionais, há também uma diferença semântica.
Delegados são, conceitualmente, modelos de função; isto é, eles expressam um contrato ao qual uma função deve aderir para serem considerados do "tipo" do delegado.
Eventos representam ... bem, eventos. Eles pretendem alertar alguém quando algo acontece e, sim, eles seguem uma definição de delegado, mas não são a mesma coisa.
Mesmo se fossem exatamente a mesma coisa (sintaticamente e no código IL), ainda restará a diferença semântica. Em geral, prefiro ter dois nomes diferentes para dois conceitos diferentes, mesmo que sejam implementados da mesma maneira (o que não significa que eu gostaria de ter o mesmo código duas vezes).
fonte
Aqui está outro bom link para se referir. http://csharpindepth.com/Articles/Chapter2/Events.aspx
Resumidamente, a retirada do artigo - Eventos são encapsulamentos sobre delegados.
Citação do artigo:
fonte
public Delegate
variável estaria expondo "dados", mas, pelo que sei, a POO nunca mencionou conceitos como aDelegate
(não é um "objeto" nem uma "mensagem") e o .NET mal trata os delegados como dados de qualquer maneira.AddXXXHandler
métodos com umaprivate Delegate
variável pode ser uma boa opção. Nesse caso, você pode verificar se um manipulador já está definido e reagir adequadamente. Essa também pode ser uma boa configuração, se você precisar que o objeto que está segurandoDelegate
seja capaz de limpar todos os manipuladores (event
não fornece nenhuma maneira de fazer isso).NOTA: Se você tiver acesso ao C # 5.0 Unleashed , leia as "Limitações no uso simples de delegados" no capítulo 18, intitulado "Eventos" para entender melhor as diferenças entre os dois.
Sempre me ajuda a ter um exemplo simples e concreto. Então aqui está um para a comunidade. Primeiro, mostro como você pode usar delegados sozinhos para fazer o que os Eventos fazem por nós. Então eu mostro como a mesma solução funcionaria com uma instância de
EventHandler
. E então eu explico por que não queremos fazer o que explico no primeiro exemplo. Este post foi inspirado em um artigo de John Skeet.Exemplo 1: Usando delegado público
Suponha que eu tenha um aplicativo WinForms com uma única caixa suspensa. O menu suspenso está vinculado a um
List<Person>
. Onde Person tem propriedades de Id, Name, NickName, HairColor. No formulário principal, há um controle de usuário personalizado que mostra as propriedades dessa pessoa. Quando alguém seleciona uma pessoa no menu suspenso, os rótulos na atualização de controle do usuário mostram as propriedades da pessoa selecionada.Aqui está como isso funciona. Temos três arquivos que nos ajudam a montar isso:
Aqui está o código relevante para cada uma das classes:
Aqui está o nosso controle de usuário:
Finalmente, temos o seguinte código em nosso Form1.cs. Aqui estamos chamando OnPersonChanged, que chama qualquer código inscrito no delegado.
Está bem. Então é assim que você faria isso funcionar sem usar eventos e apenas usando delegados . Acabamos de colocar um representante público em uma classe - você pode torná-lo estático ou único, ou o que for. Ótimo.
MAS, MAS, MAS, nós não queremos fazer o que acabei de descrever acima. Porque os campos públicos são ruins por muitos, muitos motivos. Então, quais são nossas opções? Como John Skeet descreve, aqui estão nossas opções:
PersonChangedDel = null
, eliminando todas as outras assinaturas. outro problema que permanece aqui é que, como os usuários têm acesso ao delegado, eles podem invocar os destinos na lista de chamadas - não queremos que usuários externos tenham acesso a quando criar nossos eventos.Esta terceira opção é essencialmente o que um evento nos oferece. Quando declaramos um EventHandler, ele nos dá acesso a um delegado - não publicamente, não como uma propriedade, mas como isso chamamos de um evento que acabou de adicionar / remover acessadores.
Vamos ver como é o mesmo programa, mas agora usando um Evento em vez do representante público (eu também mudei nosso Mediador para um singleton):
Exemplo 2: Com EventHandler em vez de um representante público
Mediador:
Observe que se você F12 no EventHandler, ele mostrará que a definição é apenas um delegado genérico com o objeto "remetente" extra:
O Controle do Usuário:
Finalmente, aqui está o código Form1.cs:
Como o EventHandler deseja e EventArgs como parâmetro, eu criei essa classe com apenas uma propriedade:
Espero que isso mostre um pouco sobre por que temos eventos e como eles são diferentes - mas funcionalmente iguais - como delegados.
fonte
The other problem that remains here is that since the users have access to the delegate, they can invoke the targets in the invocation list -- we don't want external users having access to when to raise our events
. Na versão mais recente doMediator
, você ainda pode chamar oOnPersonChange
sempre que tiver uma referência ao singleton. Talvez você deva mencionar que aMediator
abordagem não impede esse comportamento específico e está mais próxima de um barramento de eventos.Você também pode usar eventos em declarações de interface, não para delegados.
fonte
Action a { get; set; }
dentro de uma definição de interface.Que grande mal-entendido entre eventos e delegados !!! Um delegado especifica um TYPE (como um
class
, ouinterface
faz), enquanto um evento é apenas um tipo de MEMBER (como campos, propriedades, etc.). E, como qualquer outro tipo de membro, um evento também tem um tipo. No entanto, no caso de um evento, o tipo do evento deve ser especificado por um delegado. Por exemplo, você NÃO PODE declarar um evento de um tipo definido por uma interface.Concluindo, podemos fazer a seguinte observação: o tipo de um evento DEVE ser definido por um delegado . Esta é a principal relação entre um evento e um delegado e é descrita na seção II.18. Definindo eventos das partições I a VI do ECMA-335 (CLI) :
No entanto, esse fato NÃO implica que um evento use um campo de delegado de apoio . Na verdade, um evento pode usar um campo de apoio de qualquer tipo diferente de estrutura de dados de sua escolha. Se você implementar um evento explicitamente em C #, poderá escolher a maneira como armazena os manipuladores de eventos (observe que os manipuladores de eventos são instâncias do tipo do evento , que por sua vez é obrigatoriamente um tipo de delegado --- da Observação anterior ) Mas, você pode armazenar os manipuladores de eventos (que são instâncias de delegação) em uma estrutura de dados como uma
List
ou outraDictionary
ou qualquer outra coisa, ou mesmo em um campo de delegado de apoio. Mas não esqueça que NÃO é obrigatório que você use um campo de delegado.fonte
Um evento no .net é uma combinação designada de um método Add e um método Remove, os quais esperam algum tipo específico de delegado. O C # e o vb.net podem gerar automaticamente código para os métodos add e remove, que definirão um delegado para armazenar as inscrições de eventos e adicionar / remover o delegado passado no / para o delegado da assinatura. O VB.net também gerará automaticamente o código (com a instrução RaiseEvent) para invocar a lista de assinaturas se e somente se não estiver vazia; por alguma razão, o C # não gera o último.
Observe que, embora seja comum gerenciar assinaturas de eventos usando um representante multicast, esse não é o único meio de fazer isso. De uma perspectiva pública, um possível assinante de evento precisa saber como informar um objeto que deseja receber eventos, mas não precisa saber qual mecanismo o editor usará para gerar os eventos. Observe também que, enquanto quem definiu a estrutura de dados do evento em .net aparentemente pensou que deveria haver um meio público de aumentá-los, nem o C # nem o vb.net fazem uso desse recurso.
fonte
Para definir um evento de maneira simples:
Evento é uma REFERÊNCIA a um delegado com duas restrições
Acima de dois, estão os pontos fracos dos delegados e são abordados no evento. Exemplo de código completo para mostrar a diferença no violinista está aqui https://dotnetfiddle.net/5iR3fB .
Alterne o comentário entre Evento e Delegado e o código do cliente que chama / atribui valores para delegar para entender a diferença
Aqui está o código embutido.
fonte
Delegado é um ponteiro de função com segurança de tipo. Evento é uma implementação do padrão de design do publicador-assinante usando delegate.
fonte
Se você marcar Intermediate Language, você saberá que o compilador .net converte delegate para uma classe selada na IL com algumas funções internas, como invoke, beginInvoke, endInvoke e delegate class herdadas de outra classe, talvez chamada "SystemMulticast". Eu acho que Event é uma classe filho de Delegado com algumas propriedades adicionais.
A diferença entre instância do evento e delegado é que você não pode executar o evento fora da declaração. Se você declarar um evento na classe A, poderá executá-lo apenas na classe A. Se você declarar um delegado na Classe A, poderá usá-lo em qualquer lugar. Eu acho que essa é a principal diferença entre eles
fonte