Entendo o objetivo dos eventos, especialmente no contexto da criação de interfaces com o usuário. Eu acho que este é o protótipo para criar um evento:
public void EventName(object sender, EventArgs e);
O que os manipuladores de eventos fazem, por que são necessários e como faço para criar um?
c#
.net
events
event-handling
Levi Campbell
fonte
fonte
Respostas:
Para entender os manipuladores de eventos, você precisa entender os delegados . Em C # , você pode pensar em um delegado como um ponteiro (ou uma referência) para um método. Isso é útil porque o ponteiro pode ser passado como um valor.
O conceito central de um delegado é sua assinatura ou forma. Esse é (1) o tipo de retorno e (2) os argumentos de entrada. Por exemplo, se criarmos um delegado
void MyDelegate(object sender, EventArgs e)
, ele poderá apontar apenas para métodos que retornamvoid
e recebem umobject
eEventArgs
. Como um buraco quadrado e uma estaca quadrada. Então, dizemos que esses métodos têm a mesma assinatura ou forma que o delegado.Portanto, sabendo como criar uma referência a um método, vamos pensar no propósito dos eventos: queremos que algum código seja executado quando algo acontecer em outro lugar do sistema - ou "manipular o evento". Para fazer isso, criamos métodos específicos para o código que queremos que seja executado. A cola entre o evento e os métodos a serem executados são os delegados. O evento deve armazenar internamente uma "lista" de ponteiros para os métodos a serem chamados quando o evento é gerado. * É claro que, para poder chamar um método, precisamos saber quais argumentos passar para ele! Usamos o delegado como o "contrato" entre o evento e todos os métodos específicos que serão chamados.
Portanto, o padrão
EventHandler
(e muitos outros semelhantes) representa uma forma específica de método (novamente, void / object-EventArgs). Quando você declara um evento, está dizendo qual forma de método (EventHandler) esse evento chamará, especificando um delegado:(* Essa é a chave para eventos no .NET e retira a "mágica" - um evento é realmente, oculto, apenas uma lista de métodos da mesma "forma". A lista é armazenada onde o evento fica. Quando o evento é "gerado", é realmente apenas "percorra esta lista de métodos e chame cada um, usando esses valores como parâmetros". Atribuir um manipulador de eventos é apenas uma maneira mais bonita e fácil de adicionar seu método a essa lista de métodos ser chamado).
fonte
C # conhece dois termos
delegate
eevent
. Vamos começar com o primeiro.Delegar
A
delegate
é uma referência a um método. Assim como você pode criar uma referência a uma instância:Você pode usar um delegado para criar uma referência a um método:
Agora que você tem essa referência a um método, pode chamá-lo através da referência:
Mas por que você faria? Você também pode ligar
myFactory.GetInstance()
diretamente. Nesse caso, você pode. No entanto, há muitos casos em que você não deseja que o restante do aplicativo tenha conhecimentomyFactory
ou liguemyFactory.GetInstance()
diretamente.Uma óbvia é se você quer ser capaz de substituir
myFactory.GetInstance()
amyOfflineFakeFactory.GetInstance()
de um lugar central (aka padrão Factory Method ).Padrão de método de fábrica
Portanto, se você tem uma
TheOtherClass
classe e precisa usar amyFactory.GetInstance()
, é assim que o código será exibido sem delegados (você precisará informarTheOtherClass
sobre o tipo da suamyFactory
):Se você usar delegados, não precisará expor o tipo da minha fábrica:
Assim, você pode atribuir um delegado a outra classe para usar, sem expor seu tipo a eles. A única coisa que você está expondo é a assinatura do seu método (quantos parâmetros você tem e quais).
"Assinatura do meu método", onde eu ouvi isso antes? O sim, interfaces !!! interfaces descrevem a assinatura de uma classe inteira. Pense nos delegados como descrevendo a assinatura de apenas um método!
Outra grande diferença entre uma interface e um delegado é que, quando você está escrevendo sua classe, não precisa dizer ao C # "esse método implementa esse tipo de delegado". Com interfaces, você precisa dizer "esta classe implementa esse tipo de interface".
Além disso, uma referência de delegado pode (com algumas restrições, veja abaixo) fazer referência a vários métodos (chamados
MulticastDelegate
). Isso significa que quando você chama o delegado, vários métodos anexados explicitamente serão executados. Uma referência a objeto sempre pode fazer referência apenas a um objeto.As restrições para a
MulticastDelegate
são que a assinatura (método / delegado) não deve ter nenhum valor de retorno (void
) e as palavras-chaveout
eref
não é usada na assinatura. Obviamente, você não pode chamar dois métodos que retornam um número e esperam que eles retornem o mesmo número. Uma vez que a assinatura esteja em conformidade, o delegado será automaticamente aMulticastDelegate
.Evento
Eventos são apenas propriedades (como os campos get; set; properties para instance) que expõem a assinatura ao delegado de outros objetos. Essas propriedades, no entanto, não suportam get; set ;. Em vez disso, eles suportam add; remover;
Então você pode ter:
Uso na interface do usuário (WinForms, WPF, UWP etc.)
Portanto, agora sabemos que um delegado é uma referência a um método e que podemos ter um evento para informar ao mundo que eles podem nos fornecer seus métodos a serem referenciados por nosso delegado, e somos um botão de interface do usuário: pode perguntar a qualquer pessoa interessada se fui clicado para registrar seu método conosco (por meio do evento exposto). Podemos usar todos os métodos que nos foram dados e consultá-los pelo nosso delegado. E então, vamos esperar ... até que um usuário chegue e clique nesse botão, teremos motivos suficientes para chamar o delegado. E como o delegado faz referência a todos os métodos que nos foram dados, todos esses métodos serão invocados. Não sabemos o que esses métodos fazem, nem sabemos qual classe implementa esses métodos. Só nos preocupamos com o fato de alguém estar interessado em sermos clicados,
Java
Idiomas como Java não têm representantes. Eles usam interfaces. A maneira como eles fazem isso é pedir a qualquer pessoa interessada em 'nos clicar', para implementar uma certa interface (com um determinado método que podemos chamar), e nos fornecer toda a instância que implementa a interface. Mantemos uma lista de todos os objetos que implementam essa interface e podemos chamar seu 'método certo que podemos chamar' sempre que clicamos.
fonte
event
é mero açúcar de sintaxe, nada mais.Essa é realmente a declaração de um manipulador de eventos - um método que será chamado quando um evento for disparado. Para criar um evento, escreva algo como isto:
E então você pode se inscrever no evento assim:
Com OnMyEvent () definido assim:
Sempre que
Foo
dispararMyEvent
, seuOnMyEvent
manipulador será chamado.Você nem sempre precisa usar uma instância
EventArgs
como o segundo parâmetro. Se você deseja incluir informações adicionais, pode usar uma classe derivada deEventArgs
(EventArgs
é a base por convenção). Por exemplo, se você observar alguns dos eventos definidosControl
no WinForms ouFrameworkElement
no WPF, poderá ver exemplos de eventos que transmitem informações adicionais aos manipuladores de eventos.fonte
OnXXX
padrão de nomeação para seu manipulador de eventos. (Estupidamente, OnXXX é entendido como 'manipular XXX' no MFC e 'aumentar XXX' em .net, e agora seu significado não é claro e confuso - consulte esta publicação para obter detalhes ). Os nomes preferidos seriamRaiseXXX
gerar eventos e /HandleXXX
ouSender_XXX
para manipuladores de eventos.Aqui está um exemplo de código que pode ajudar:
fonte
OnMaximum?.Invoke(this,new MyEventArgs("you've entered..."));
Apenas para adicionar as ótimas respostas existentes aqui - com base no código do aceito, que usa um
delegate void MyEventHandler(string foo)
...Como o compilador conhece o tipo de delegado do evento SomethingHappened , isso:
É totalmente equivalente a:
E os manipuladores também podem ser não registrados com o
-=
seguinte:Por uma questão de completude, aumentar o evento pode ser feito assim, apenas na classe proprietária do evento:
A cópia local do encadeamento do manipulador é necessária para garantir que a chamada seja segura para encadeamento - caso contrário, um encadeamento poderá cancelar o registro do último manipulador do evento imediatamente depois de verificarmos se foi
null
e teríamos uma "diversão"NullReferenceException
lá .O C # 6 apresentou uma boa mão curta para esse padrão. Ele usa o operador de propagação nula.
fonte
Minha compreensão dos eventos é;
Delegar:
Uma variável para conter referência ao método / métodos a serem executados. Isso torna possível passar métodos como uma variável.
Etapas para criar e chamar o evento:
O evento é uma instância de um delegado
Como um evento é uma instância de um delegado, primeiro precisamos definir o delegado.
Atribua o método / métodos a serem executados quando o evento for disparado ( chamando o delegado )
Dispare o evento ( ligue para o delegado )
Exemplo:
fonte
editor: onde os eventos acontecem. O Publicador deve especificar qual delegado a classe está usando e gerar argumentos necessários, passar esses argumentos e ele próprio para o delegado.
assinante: onde a resposta acontece. O assinante deve especificar métodos para responder a eventos. Esses métodos devem usar o mesmo tipo de argumento que o delegado. O assinante adiciona esse método ao delegado do editor.
Portanto, quando o evento ocorrer no editor, o delegado receberá alguns argumentos do evento (dados, etc.), mas o editor não tem idéia do que acontecerá com todos esses dados. Os assinantes podem criar métodos em sua própria classe para responder a eventos na classe do editor, para que os assinantes possam responder aos eventos do editor.
fonte
fonte
Concordo com o KE50, exceto que vejo a palavra-chave 'event' como um alias para 'ActionCollection', já que o evento contém uma coleção de ações a serem executadas (por exemplo, o delegado).
fonte
Ótimas respostas técnicas no post! Tecnicamente, não tenho nada a acrescentar.
Uma das principais razões pelas quais os novos recursos aparecem nos idiomas e no software em geral é o marketing ou a política da empresa! :-) Isso não deve ser subestimado!
Eu acho que isso se aplica em certos casos aos delegados e eventos também! Acho-os úteis e agrego valor à linguagem C #, mas, por outro lado, a linguagem Java decidiu não usá-las! eles decidiram que o que você está resolvendo com os delegados já pode resolver com os recursos existentes da linguagem, ou seja, interfaces, por exemplo
Por volta de 2001, a Microsoft lançou o framework .NET e a linguagem C # como uma solução concorrente para Java, então era bom ter NOVOS RECURSOS que o Java não possui.
fonte
Recentemente, fiz um exemplo de como usar eventos em c # e publiquei no meu blog. Tentei deixar o mais claro possível, com um exemplo muito simples. Caso isso possa ajudar alguém, aqui está: http://www.konsfik.com/using-events-in-csharp/
Inclui descrição e código fonte (com muitos comentários), e se concentra principalmente no uso adequado (semelhante ao modelo) de eventos e manipuladores de eventos.
Alguns pontos-chave são:
Os eventos são como "subtipos de delegados", apenas mais restritos (no bom sentido). De fato, a declaração de um evento sempre inclui um delegado (EventHandlers é um tipo de delegado).
Manipuladores de eventos são tipos específicos de delegados (você pode pensar neles como um modelo), que forçam o usuário a criar eventos com uma "assinatura" específica. A assinatura está no formato: (remetente do objeto, EventArgs eventarguments).
Você pode criar sua própria subclasse de EventArgs, a fim de incluir qualquer tipo de informação que o evento precise transmitir. Não é necessário usar EventHandlers ao usar eventos. Você pode ignorá-los completamente e usar seu próprio tipo de delegado no lugar deles.
Uma diferença importante entre o uso de eventos e delegados é que os eventos só podem ser chamados de dentro da classe em que foram declarados, mesmo que possam ser declarados como públicos. Essa é uma distinção muito importante, pois permite que seus eventos sejam expostos para que sejam "conectados" a métodos externos, enquanto ao mesmo tempo eles são protegidos contra "uso indevido externo".
fonte
Outra coisa a saber , em alguns casos, você deve usar os Delegados / Eventos quando precisar de um baixo nível de acoplamento !
Se você deseja usar um componente em vários locais do aplicativo , é necessário criar um componente com baixo nível de acoplamento e a LÓGICA despreocupada específica deve ser delegada FORA do seu componente! Isso garante que você tenha um sistema desacoplado e um código mais limpo.
No princípio SOLID , este é o " D ", ( princípio de inversão da dependência de D ).
Também conhecido como " IoC ", Inversão de controle .
Você pode criar " IoC " com eventos, delegados e DI (injeção de dependência).
É fácil acessar um método em uma classe filho. Mas é mais difícil acessar um método em uma classe pai a partir de filho. Você precisa passar a referência dos pais para a criança! (ou use DI com interface)
Delegados / Eventos nos permitem comunicar da criança para os pais sem referência!
Neste diagrama acima, eu não uso Delegado / Evento e o componente pai B deve ter uma referência do componente pai A para executar a lógica de negócios despreocupada no método A. (alto nível de acoplamento)
Com essa abordagem, eu teria que colocar todas as referências de todos os componentes que usam o componente B! :(
Neste diagrama acima, eu uso Delegate / Event e o componente B não precisa conhecer A. (baixo nível de acoplamento)
E você pode usar seu componente B em qualquer lugar do seu aplicativo !
fonte