czwartek, 1 grudnia 2011

[RX 3] Reactive Extensions pierwszy kontakt z kodem (czyli artykułu cz. 3) [PL]

W ramach serii postów dotyczących Reactive Extensions przyjrzeliśmy się już problemom związanych z asynchronicznością oraz przyjrzeliśmy się bliżej kolekcjom i wzorcu obserwatora (IObserver, IObservable). W tym wpisie zobaczymy pierwszy przykład kodu wykorzystującego RX.
Jak wspominałem wcześniej, w .NET 4.0 jest wbudowane pewne wsparcie dla IObserver i IObservable. Jednak są to tylko definicje wspomnianych interfejsów, aby wykorzystać pełne możliwości RX, trzeba je zainstalować i dołączyć do projektu, widać to na poniższym rysunku:
O pobieraniu i instalacji RX pisałem już wcześniej i najłatwiej to chyba zrobić przy pomocy NuGet'a. Wpisujemy „rx” do wyszukiwarki, czekamy chwilę i już możemy instalować:
Koniecznie należy zainstalować „Rx-Main”, ale przydać się mogą i inne biblioteki. Np. jeżeli tworzymy aplikację wykorzystującą WPF, to warto doinstalować również „Rx-WPF” (by wygodnie obserwować na wątku Dispatcher'a).
Po instalacji RX znajdziemy w projekcie referencję do assembly „System.Reactive”, w której znajdziemy wiele interesujących rozszerzeń i klas stanowiących siłę RX:
Przejdźmy jednak do obiecywanego kodu. Zacznijmy od bardzo prostego przykładu, w którym prostą tablicę przekształcimy na IObservable i wypiszemy jej zawartość:
using System;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;

namespace RXExamples
{
  class Program
  {
    static void Main( string[] args )
    {
      var char_array = "Hello world!!".ToArray();
      IObservable<char> obs = char_array.ToObservable( );
      using ( obs.Subscribe( Console.WriteLine ) )
      {
        Console.WriteLine( "Hit Enter to finish" );
        Console.ReadLine();
      }
    }
  }
}
W tym przykładzie zamieniliśmy tablicę znaków z napisem „Hello world!!” na IObservable przy użyciu funkcji ToObservable. Następnie zasubskrybowaliśmy nasz obiekt typu IObservable (funkcja Subscribe) i jako parametr przekazaliśmy Console.WriteLine, czyli delegację wypisującą otrzymywane znaki na okno konsoli. W wyniku działania powyższego kodu otrzymamy:
H
e
l
l
o

w
o
r
l
d
!
!
Hit Enter to finish
Oczywiście nie ma tu nic niezwykłego, ktoś mógłby stwierdzić, że to tylko trudniejsze wypisanie wszystkich elementów tablicy. Prosta jednowątkowa aplikacja, ale.... wystarczy dopisać tylko prosty element, by obserwacja odbywała się na osobnym wątku. W tym celu można wykorzystać metodę rozszerzającą ObserveOn lu przekazać pewien parametr do metody ToObservable. Wspomnianym parametrem może być np. Scheduler.NewThread. W ten sposób wykonując poniższy kod:
static void Main( string[] args )
{
  var char_array = "Hello world!!".ToArray();
  IObservable<char> obs = char_array.ToObservable( Scheduler.NewThread );
  using ( obs.Subscribe( Console.WriteLine ) )
  {
    Console.WriteLine( "Hit Enter to finish" );
    Console.ReadLine();
  }
}
Tym razem otrzymamy:
Hit Enter to finish
H
e
l
l
o

w
o
r
l
d
!
!
Jak widać tym razem napis „Hit Enter to finish” pojawił się przed wypisaniem znaków z obserwowanej tablicy, a znaki z tablicy zostały wypisane w osobnym wątku. Można to również sprawdzić debugując aplikację i przeglądając watki:
Jak widać, subskrypcja odbywa się na osobnym wątku (Worker Thread), w odróżnieniu o wątku aplikacji (Main Thread).
Przedstawiony przykład może wydawać się prosty i banalny, ale to dopiero początek ... :)
cdn...
Promuj

4 komentarze:

  1. Ciekawy tekst. Tylko jest moment którego nie rozumiem, chodzi o:

    using ( obs.Subscribe( Console.WriteLine ) )
    {
    Console.WriteLine( "Hit Enter to finish" );
    Console.ReadLine();
    }

    <- zgodnie z opisem, argumentem Subscribe powinien być instancja obiektu dziedziczącego po interfejsie IObserver - tymczasem jako argument Subscribe podajemy ... delegat na funkcję Console.WriteLine
    O co tu chodzi? :)

    I jak to się dzieje ze ReadLine odczytuje wartości które wcześniej zostały wpisane przez WriteLine?

    OdpowiedzUsuń
  2. Cześć,
    Zgadza się zgodnie z poprzednim postem (w którym opisywany był wzorzec obserwator) argumentem dla Subscribe powinna być instancja obiektu dziedziczącego po interfejsie IObserver i właśnie taka jest definicja Subscribe dla IObservable. RX pozwala jednak na pewne uproszczenia, otóż subskrybując można przekazać do Subscribe również delegat do obsługi OnNext lub nawet wszystkie trzy delegaty: OnNext, OnError, OnCompleted. Dzięki takiemu podejściu nie musimy sami tworzyć IObserver, a zostanie to zrobione "za nas", na podstawie przekazanych delegat.

    Jeżeli chodzi natomiast o "ReadLine, które odczytuje wartości wypisane wcześniej przez WriteLine", to nic takiego nie ma tu miejsca. "ReadLine" jest tu tylko po to, żeby oczekiwać na interakcję ze strony użytkownika, a w szczególności naciśnięcie klawisza Enter, który zakończy tą aplikację.

    Mam nadzieję , że to trochę rozwiało wątpliwości.

    OdpowiedzUsuń
  3. Rozumiem, czyli Console.WriteLine jest tu delegatem do obsługi OnNext.
    To wszystko wyjaśnia. Dzięki za info.
    Muszę sobie ten przykład przerobić w wolnej chwili.

    OdpowiedzUsuń

Posty powiązane / Related posts