Olá galera, tudo certin? Depois de um passeio pelo mundo da Intermediate Language, resolvi voltar a abordar assuntos mais “altos”. Hoje, quero falar um pouquinho sobre Reactive Extensions (mais conhecido por Rx), ou ainda, Linq to Events.
Antes de qualquer coisa, meu reconhecimento a Erik Meijer. Para quem não sabe quem é o cara, ele é o “pai” do Linq e do Rx dentro da Microsoft. Recomendo fortemente qualquer vídeo com ele no Channel9.
Importante: Esse post não tem a proposta de apresentar fundamentos. Vou aguardar o feedback da comunidade para iniciar uma série 101 para esse assunto.
O que é o Reactive Extensions?
A página oficial do produto define Rx como:
- uma biblioteca para composição assíncrona de aplicações baseadas em eventos usando coleções observáveis;
- uma conjunto extendido de operadores LINQ que possibilita computação assíncrona/baseada em eventos como “push-based”, através das novas interfaces do .NET 4 IObservable
e IObserver .
Entendeu?! Não!? Nem eu quando li pela primeira vez. Por isso, esse artigo é todo baseado em exemplos
Importante: Antes de continuar, faça o download da extensão de bibliotecas na página do Reactive Extensions
Eu acredito na programação declarativa
Para quem chegou de marte, temos duas formas de programar um computador. Podemos ser:
- imperativos – dizendo ao computador o que queremos que ele faça, e como queremos que ele faça. abordagem tradicional com For/If/While;
- declarativos – dizendo ao computador o que queremos que ele faça e deixando que ele escolha como fazer. fazemos isso há algum tempo usando SQL e afins.
Há uma certa tendência, ao meu ver, de migrar as tecnologias de desenvolvimento para a “forma declarativa”. Rx é um passo gigante nessa direção.
Nosso aplicativo de testes
Para ilustrar Rx, usarei uma aplicação base. Para criá-la:
- Inicie um novo Windows Console no VS2010;
- Adicione referências para System.Windows.Forms e System.Drawing;
- Adicione referências para System.CoreEx e System.Reactive. (Lembre-se de instalar as extensões que estão disponíveis no site oficial do Reactive Extensions antes de começar; Lembre-se também de mudar o Target Framework para .NET Framework 4)
- Substitua o código de Program.cs por esse:
Um exemplo simples
Para começar, uma tarefa trivial… Queremos imprimir no console as coordenadas do ponteiro enquanto ele estiver sobre a janela.
Na forma tradicional, escreveríamos algo assim:
Simples . bem simples .. Mas nem por isso bom!! Vejamos o porquê:
- Eventos são fontes de dados ocultas. Precisamos, obrigatóriamente de um handler para ter acesso aos dados.
- Eventos sempre são submetidos ao tratamento do handler (se quiséssemos restringir o domínio de pontos tratados pelo código do handler, deveríamos colocar um código de aceite logo no início);
- Eventos não suportam composição (pelo menos, não de forma fácil). Se desejássemos encaminhar os pontos recebidos pelo nosso evento, deveríamos colocar uma chamada explícita para o método de seqüência no corpo de código do handler;
- Eventos requerem manutenção manual de assinatura (Quantos objetos ficam pendurados na memória por assinarem eventos e não cancelarem a assinatura).
Agora, a forma Rx para o código acima:
Diferente.. parece até mais complexo .. Mas melhor!! Vejamos o porquê:
- Tiramos o evento de cena! Agora, estamos operando sobre uma coleção de pontos.
- Embora estejamos sempre executando uma Action, ela está vinculada a “coleção” de pontos observáveis, que pode ser filtrada via LINQ (próximos exemplos)
- Nossa varíavel mouseDown suporta composição. Ou seja, posso usar todos os operadores LINQ para processar os elementos que “saem” de mouseDown;
- Tenho mais facilidade em fazer o CleanUp, toda inscrição tem um dispose que “desassina” automaticamente o evento.
O que quero dizer com usar operadores LINQ?
Exatamente isso. Veja o código que segue:
Olha lá o Rx mostrando sua força! Observe que eu não precisei adicionar uma condição no tratador. Antes de tudo, eu “declarei” que pontos com X < 50 não deveriam ser considerados. Além disso, observe que eu simplifiquei o tratamento: Se apenas a localização é importante para meu Handler, por que eu estava passando TODO o args…
Legal, né?
Empilhando Querys aos Eventos
Como você faria para implementar uma operação de Drag usando Windows Forms? Seguramente, você precisaria assinar os eventos MouseDown, MouseMove e MouseUp. Além disso, precisaria manter um registro do ponto onde ocorreu o MouseDown (variável de classe). Mais ainda, precisaria manter um flag que indicasse se estaria ou não ocorrendo um drag. Certo? Sim, antes do Rx..
O código a seguir mostra como imprimir as coordenadas do ponteiro enquanto o botão do mouse permanecer pressionado (base para dragging):
Simplesmente, lindo!
Por fim, um exemplo matador
Combinando Rx com um pouquinho de tipos anônimos…
podemos fazer isso:
Por hoje, é isso?!
Rodrigo Vieira
02/09/2010
Po, bonito mesmo. Bem escolhido o exemplo.
Há uns 5 anos eu JAMAIS sonharia que um dia o C# seria uma linguagem tão expressiva..achava que só veria coisa assim em linguagens dinâmicas tipo Ruby.
Daniel Moreira Yokoyama
02/09/2010
Muito bom. Excelente técnica, e ótimo exemplo. Adorei conhecer isso.
tucaz
02/09/2010
Massa!
Essa extensão já é final?
elemarjr
02/09/2010
O core para essa extensão são as interfaces IObservable e IObserve. Ambas estão no Framework 4. No mais, a coisa continua evoluindo no Microsoft Research, mas já são versões RTM
Israel Aece
02/09/2010
Boas Elemar,
Um outro benefício que temos também é a possibilidade de integrar o Rx com a programação assíncrona, tornando ela muito mais simplificada.