Descomplicando Events, Delegates e Event Handlers em .NET
Events, delegates e EventHandlers em C# são ferramentas muito importantes e poderosas mas que podem ser complexas e difíceis de entender em um primeiro momento, o que acaba impedindo algumas pessoas de as utilizarem em suas aplicações. A ideia deste tutorial é tentar simplificar um pouco esses conceitos, espero que gostem :) | |||||||||||||||||||||||||||||||||||
Events | |||||||||||||||||||||||||||||||||||
Eventos nada mais são do que um mecanismo de comunicação entre classes, permitindo que sejam enviadas mensagens específicas de uma classe para outra sem que essas classes tenham qualquer vínculo, o que viabiliza a criação de aplicações desacopladas e de fácil manutenção. | |||||||||||||||||||||||||||||||||||
Essencialmente, existe uma classe (chamada de publisher ou event sender) responsável por disparar um evento, e outras classes (subscribers ou event receivers) preparadas para receber este tipo de evento, ou seja, classes que ficam esperando o disparo de um determinado evento para que possam realizar determinada ação, independente de onde esse evento esteja vindo. | |||||||||||||||||||||||||||||||||||
Trazendo para o mundo real, podemos fazer uma comparação com o toque do interfone ou campainha em nossas casas, por exemplo. Sempre que o interfone toca, nós realizamos a ação de ir atende-lo, sem que a gente saiba quem está emitindo esse evento de toca-lo. Ou seja, estamos preparados para receber esse tipo de evento (toque do interfone) e, sempre que ele acontece, realizamos a ação a atender. | |||||||||||||||||||||||||||||||||||
Delegates | |||||||||||||||||||||||||||||||||||
Delegates são, essencialmente, tipos que fazem referência a um método específico, definindo o retorno deste método e os seus parâmetros. Em outras palavras, considerando o contexto de envio de eventos, são os delegates que definem a assinatura do método que será chamado pelo subscriber sempre que um evento específico definido por esse delegate for enviado pelo publisher. | |||||||||||||||||||||||||||||||||||
Sei que esse conceito parece um pouco confuso, mas vai ficar mais claro olhando o exemplo mais abaixo. | |||||||||||||||||||||||||||||||||||
EventHandlers | |||||||||||||||||||||||||||||||||||
Com a ampla utilização dos delegates no contexto de eventos, o próprio .NET, a partir do .NET Framework 3.0, criou um delegate específico chamado EventHandler, que abstrai a criação de um delegate e já entrega a representação de um método que vai tratar eventos específicos. | |||||||||||||||||||||||||||||||||||
Agora, vamos a um exemplo utilizando delegates e events e depois uma demonstração da abstração que o EventHandler nos oferece. | |||||||||||||||||||||||||||||||||||
Exemplo | |||||||||||||||||||||||||||||||||||
Vamos imaginar um cenário em que temos um serviço de pagamentos de um e-commerce que, sempre que um pagamento for feito, seja necessário enviar um e-mail para o comprador informando que seu pagamento foi realizado com sucesso. | |||||||||||||||||||||||||||||||||||
Para isso, criei um projeto Console simples com as seguintes classes: | |||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
PaymentService.cs | |||||||||||||||||||||||||||||||||||
Para que um evento seja lançado, precisamos basicamente seguir 3 passos: | |||||||||||||||||||||||||||||||||||
1. Criar o delegate responsável por definir a assinatura (tipo de retorno e argumentos) do método que será chamado pelo publisher para enviar o evento e do método invocado pelo subscriber depois que esse evento é emitido. Os delegates recebem como parâmetro um objeto (representando a classe responsável por enviar o evento) e os dados correspondentes a esse evento (nesse exemplo, criei uma classe customizada chamada PaymentEventArgs que herda de EventArgs - por padrão os delegates recebem aqui qualquer classe que herde de EventArgs) | |||||||||||||||||||||||||||||||||||
2. Definir um evento baseado nesse delegate | |||||||||||||||||||||||||||||||||||
3. Invocar o evento por meio de um método chamado de event publisher que por convenção do .NET Framework deve ser protected, virtual e retornar void, além de possuir um nome composto por On + nome do evento (OnPaymentMade, por exemplo). | |||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
O método MakeAPayment é o responsável por realizar o pagamento (nesse caso, estou somente simulando que um pagamento foi enviado) e chamar o event publisher que emite o evento. Lembrando que ele só sabe qual evento enviar graças ao nosso acordo firmado no delegate, que vai permitir que tanto o publisher saiba qual evento enviar quanto o subscriber (EmailService) saiba qual evento receber. | |||||||||||||||||||||||||||||||||||
Além disso, é importante frisar que o PaymentService não conhece os métodos que vão receber o evento enviado (que nesse caso é o serviço de e-mail), favorecendo o desacoplamento entre as classes. | |||||||||||||||||||||||||||||||||||
EmailService.cs | |||||||||||||||||||||||||||||||||||
Aqui no serviço de envio de e-mails, temos um método SendEmail que será encarregado por receber os eventos enviados pelo serviço de pagamento. Este método deve seguir a assinatura especificada pelo delegate, ou seja, retornar void e receber como parâmetros o objeto responsável por enviar o evento e os argumentos daquele evento específico que, nesse caso, foi o PaymentEventArgs. Se este método tivesse como argumentos somente o objeto source e um EventArgs genérico, sendo criado como, por exemplo, public void SendEmail(object source, EventArgs e), ele não receberia o evento de pagamento enviado por não seguir as regras especificadas pelo delegate. | |||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
PaymentEventArgs.cs | |||||||||||||||||||||||||||||||||||
Classe que representa um EventArgs customizado, permitindo passar dados de um publisher para um subscriber por meio de um evento. | |||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
Payment.cs | |||||||||||||||||||||||||||||||||||
Classe que representa um pagamento. | |||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
Program.cs | |||||||||||||||||||||||||||||||||||
Um outro ponto importante para que todo esse fluxo de envio e recebimento de eventos funcione, é fazer o registro dos subscribers ao evento. E aqui eu falo subscribers no plural porque podemos facilmente criar outros métodos responsáveis por realizar outras ações específicas como enviar um SMS, por exemplo, e registrá-los também ao evento de pagamentos. | |||||||||||||||||||||||||||||||||||
Para fazer esse registro, é necessário utilizar a notação evento += handler (esse += não é a mesma coisa que a abreviação que usamos quando estamos somando um valor a uma variável e atribuindo a ela mesma, é uma notação específica para registrar subscribers à eventos). | |||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
Executando a aplicação com os arquivos demonstrados acima, obtemos como resposta no console o texto abaixo, demonstrando que, logo após a finalização do pagamento, foi chamado o serviço de envio de e-mails: | |||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
Utilizando o EventHandler | |||||||||||||||||||||||||||||||||||
Conforme dito anteriormente, a partir do .NET Framework 3.0 foi implementado o delegate EventHandler, responsável por abstrair a criação de um delegate e um event em um só método. Implementando esse delegate, nosso PaymentService ficaria de seguinte maneira: | |||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
Atualmente, essa última implementação utilizando o EventHandler é a mais utilizada. Além disso, é possível, também, criar um EventHandler sem um tipo específico de EventArgs (o que não permitiria passar dados de um evento para um subscriber) usando a seguinte assinatura public event EventHandler PaymentMade. | |||||||||||||||||||||||||||||||||||
Conclusão | |||||||||||||||||||||||||||||||||||
Events e delegates são muito utilizados principalmente em situações onde desejamos desacoplar os serviços de nossas aplicações, facilitando a manutenção e os testes. Espero que tenha ficado um pouco mais claro sobre como funciona o fluxo de envio e recebimento de eventos e que você consiga aplicar nos seus projetos. | |||||||||||||||||||||||||||||||||||
Bons estudos! |