Explorando eventos do F#
Os eventos F# são um mecanismo poderoso que permite implementar a programação orientada a eventos em seu código F# , permitindo a comunicação e a coordenação entre diferentes partes de seu aplicativo.
Neste artigo, abordaremos como criar eventos F# e como usá-los. Ao iniciarmos este tutorial, discutiremos alguns dos fundamentos da manipulação de eventos em F#, como definir e gerar eventos.
Depois disso, discutiremos como assinar eventos e como lidar com eventos usando funções anônimas , entre outras coisas.
Noções básicas sobre eventos do F#
Os eventos F# notificam você e/ou outros objetos quando uma determinada ação ocorre. Os eventos são um recurso poderoso que pode ser utilizado em vários cenários, como atualização de interfaces de usuário, tratamento de entrada de usuário ou execução de processamento em segundo plano.
Eventos F# podem ser usados para comunicação entre classes , permitindo que enviem e recebam mensagens. Por exemplo, em um aplicativo GUI, os eventos podem representar ações como pressionar o teclado, clicar ou mover o mouse ou até mesmo eventos gerados pelo sistema, como notificações. É importante que seu aplicativo manipule esses eventos para responder adequadamente quando eles ocorrerem, assim como lidar com interrupções ou comunicação entre processos.
Os objetos em F# podem se comunicar uns com os outros por meio da transmissão síncrona de mensagens, permitindo que eles troquem informações e coordenem suas ações. Isso pode ser obtido anexando uma função de retorno de chamada, também conhecida como manipulador de eventos, a um evento que é acionado por outra função ou objeto. Quando o evento é acionado, a função callback é executada, permitindo que você responda ao evento de acordo.
Os eventos F# permitem que você implemente comunicação e coordenação eficientes entre diferentes partes do seu aplicativo, promovendo baixo acoplamento, separação de preocupações e extensibilidade. Aproveitando o poder dos eventos, você pode aprimorar a funcionalidade e a capacidade de resposta de seus aplicativos F#, tornando-os mais robustos e fáceis de usar.
A classe e o módulo de evento
A classe Control.Event<T> ajuda a criar eventos observáveis.
Para trabalhar com os eventos, ele possui os seguintes membros de instância:
Membro | Visão geral |
Publicar | Um valor de primeira classe é publicado quando uma observação é feita. |
Acionar | Este método aciona uma observação com base nos parâmetros que você fornece. |
Usando o módulo Control.Event , você pode gerenciar fluxos de eventos por meio das seguintes funções:
Valor | Visão geral |
add : ('T → unidade) → Evento<'Del,'T> → unidade | Sempre que um determinado evento é acionado, a função especificada é executada cada vez que é acionada. |
escolha: (opção 'T → 'U) → IEvent<'Del,'T> → IEvent<'U> | Nesta função, você obterá um novo evento que acontecerá quando uma seleção de mensagens do evento original for recebida. Com a função de seleção, você pode pegar uma mensagem original e criar uma nova mensagem opcional a partir dela. |
filtro: ('T → bool) → IEvent<'Del,'T> → IEvent<'T> | Nesta função, retornaremos um novo evento acionado pelo evento original, porém, o evento resultante só será acionado se o argumento do evento original não passar pela função especificada. |
map : ('T → 'U) → IEvent<'Del, 'T> → IEvent<'U> | Essa função é responsável por retornar um novo evento que passa valores que foram transformados pela função dada. |
merge : IEvent<'Del1,'T> → IEvent<'Del2,'T> → IEvent<'T> | Este evento é acionado quando o evento de entrada ou o evento de saída é acionado. |
pairwise : IEvent<'Del,'T> → IEvent<'T * 'T> | Essa função retorna um novo evento que é acionado no segundo acionamento do evento de entrada e também em qualquer acionamento subsequente. Este é o enésimo acionamento do evento de entrada, que passa os argumentos dos N-1 e N° acionadores como um par para o enésimo acionamento. Até que ocorra o enésimo disparo do evento, o argumento passado para o N-1º disparo será mantido em um estado interno oculto até que ocorra o enésimo disparo. |
partição: ('T → bool) → IEvent<'Del,'T> → IEvent<'T> * IEvent<'T> | Haverá dois eventos resultantes gerados por esta função. Na primeira instância, o evento será disparado se a aplicação do predicado aos argumentos do evento retornar verdadeiro, e na segunda instância, o evento será disparado se o predicado não retornar verdadeiro. |
scan : ('U → 'T → 'U) → 'U → IEvent<'Del,'T> → IEvent<'U> | Ele produz um novo evento aplicando a função acumulativa fornecida a valores sucessivos armazenados no evento de entrada para obter os resultados da adição desses valores sucessivos ao evento de entrada. O valor atual de um parâmetro de estado pode ser encontrado em um item de estado interno. Como a função de acumulação não bloqueia o estado interno, deve-se tomar cuidado para garantir que a entrada IEvent não seja acionada simultaneamente por vários threads. |
split : ('T → Choice<'U1,'U2>) → IEvent<'Del,'T> → IEvent<'U1> * IEvent<'U2> | Ao aplicar a função aos argumentos do evento, a função retorna dois eventos: um evento para Choice1Of2 e outro para Choice2Of2. |
Criando Eventos em F#
Para criar e usar eventos, você precisa usar a classe Event.
Um novo evento pode ser criado usando o construtor Event.
type Worker(name : string, shift : string) = let mutable _name = name; let mutable _shift = shift; let nameChanged = new Event<unit>() (* creates event *) let shiftChanged = new Event<unit>() (* creates event *) member this.Name with get() = _name and set(value) = _name <- value member this.Shift with get() = _shift and set(value) = _shift <- value
Posteriormente, você precisa expor o campo nameChanged como um membro público da definição de evento para que os ouvintes possam se conectar ao evento quando ele ocorrer, o que pode ser feito usando a propriedade Publish da definição de evento:
type Worker(name : string, shift : string) = let mutable _name = name; let mutable _shift = shift; let nameChanged = new Event<unit>() // creates event let shiftChanged = new Event<unit>() // creates event member this.NameChanged = nameChanged.Publish (* exposed event handler *) member this.ShiftChanged = shiftChanged.Publish (* exposed event handler *) member this.Name with get() = _name and set(value) = _name <- value nameChanged.Trigger() // invokes event handler member this.Shift with get() = _shift and set(value) = _shift <- value shiftChanged.Trigger() // invokes event handler
Após a criação dos manipuladores de eventos, você deve adicionar callbacks a eles.
A classe do manipulador de eventos é do tipo IEvent<T> , que fornece vários métodos:
Método | Visão geral |
val Adicionar: evento:('T → unidade) → unidade | O evento está conectado a uma função de ouvinte. Os ouvintes serão invocados quando os eventos forem acionados. |
val AddHandler : 'del → unidade | O objeto delegado é usado para conectar um manipulador ao evento. O método RemoveHandler pode ser usado para remover um manipulador posteriormente. Assim que o evento for acionado, o listener será chamado. |
val RemoveHandler : 'del → unidade | Exclui um delegado de ouvinte da lista de ouvintes para um evento. |
Um exemplo completo disso pode ser encontrado abaixo.
Aqui está um exemplo demonstrando como as técnicas e conceitos discutidos acima podem ser aplicados:
Example:
A saída será:
Worker changed name! New name: Ben Worker changed name! New name: Alex -- Another handler attached to NameChanged! Worker changed shift! New shift: Morning Worker changed shift! New shift: Night -- Another handler attached to ShiftChanged!
Exemplo de Explicação
Neste exemplo, criamos um tipo chamado Worker que possui um nome e um turno mutáveis. Também criamos dois eventos chamados nameChanged e shiftChanged usando o tipo Event . Esses eventos são usados para notificar quando o nome e o turno de um trabalhador mudam.
Como programador, criamos uma instância do tipo Worker e atribuímos à variável worker. Em seguida, anexamos um manipulador ao evento NameChanged chamando o método Add no evento e passando uma expressão lambda que imprime uma mensagem no console quando o nome é alterado.
Depois disso, alteramos o nome do trabalhador atribuindo um novo valor à propriedade Nome . Isso chama o método Trigger do evento nameChanged, que por sua vez chama o manipulador anexado e imprime a mensagem no console.
Em seguida, anexamos outro manipulador ao evento NameChanged chamando o método Add novamente e passando uma expressão lambda diferente que imprime uma mensagem diferente no console quando o nome é alterado. Mudamos o nome do trabalhador novamente e ambos os manipuladores são invocados na ordem em que foram anexados.
Em seguida, anexamos um manipulador ao evento ShiftChanged da mesma forma que fizemos com o evento NameChanged. Mudamos o turno do trabalhador atribuindo um novo valor à propriedade Shift. Isso chama o método Trigger do evento shiftChanged, que chama o manipulador anexado e imprime a mensagem no console.
Por fim, anexamos outro manipulador ao evento ShiftChanged chamando o método Add novamente e passando uma expressão lambda diferente que imprime uma mensagem diferente no console quando o turno muda. Mudamos o turno do trabalhador novamente e ambos os manipuladores são invocados na ordem em que foram anexados.
Benefícios do F# Events
Os eventos F# oferecem vários benefícios que os tornam uma ferramenta valiosa na programação orientada a eventos:
- Os eventos F# permitem que você implemente a comunicação assíncrona entre diferentes partes do seu aplicativo. Isso significa que diferentes componentes do seu aplicativo podem se comunicar entre si sem bloquear ou aguardar a conclusão um do outro, permitindo o processamento simultâneo e paralelo.
- Os eventos F# promovem um baixo acoplamento entre diferentes partes do seu aplicativo. Os produtores de eventos (objetos que geram eventos) e os consumidores de eventos (objetos que se inscrevem em eventos) são fracamente acoplados, o que significa que eles não precisam conhecer os detalhes de implementação um do outro. Isso promove modularidade e flexibilidade em seu código, tornando mais fácil modificar ou estender seu aplicativo sem afetar outras partes.
- Os eventos F# permitem que você separe preocupações e responsabilidades em seu código. Os produtores de eventos são responsáveis por gerar eventos e os consumidores de eventos são responsáveis por manipular eventos. Isso permite que você divida as responsabilidades de seu aplicativo em unidades menores e mais gerenciáveis, tornando sua base de código mais sustentável e fácil de entender.
- Os eventos F# fornecem uma maneira de estender a funcionalidade do seu aplicativo, permitindo que componentes externos assinem eventos e reajam a eles. Isso facilita a adição de novos recursos ou comportamento ao seu aplicativo sem modificar o código existente, promovendo extensibilidade e flexibilidade.
Conclusão
Os eventos F# são um recurso poderoso que permite implementar comunicação e coordenação entre objetos e classes em seus aplicativos. Eles fornecem uma maneira de notificar um ou mais objetos quando uma determinada ação ocorreu, permitindo o tratamento eficiente de entrada do usuário, eventos gerados pelo sistema e outros cenários . Ao anexar funções de retorno de chamada a eventos, você pode responder a eventos de maneira oportuna e aprimorar a funcionalidade e a capacidade de resposta de seus aplicativos F#. Esteja você criando interfaces de usuário, manipulando entradas ou executando processamento em segundo plano, os eventos podem ser uma ferramenta valiosa em seu arsenal de programação F#. Portanto, não hesite em aproveitar o poder dos eventos em seu código F# para criar aplicativos mais robustos, extensíveis e fáceis de usar.