Artykuł ten jest częścią serii artykułów na temat Programowania reaktywnego.
Zapraszam na GitHub-a.
Tematy
- Wstęp
- Zabawa z czasem - Timer
- Kto za tym stoi? - Scheduler
- Nie zapominaj - Subscribe
- Zabawa z czasem - Interval
- Zabawa z czasem - Buffer
- Zabawa z czasem - Delay
- Zabawa z czasem - Sample
- Zabawa z czasem - Throttle
- Zabawa z czasem - Timestamp/TimeInterval
- Tworzymy dane - Generators
- Tworzymy dane - Własna klasa publikująca
- Marudzimy - Skip
- Marudzimy - Take
- Łap To! - ConsoleKey
- Kombinatorzy - Concat
- Kombinatorzy - Repeat
- Kombinatorzy - Start With
- Kombinatorzy - Ambiguous
- Kombinatorzy - Merge
- Kombinatorzy - Zip
- Kombinatorzy - Switch
- Kombinatorzy - When-And-Then
- Kombinatorzy - Combine Latest
- Transformers - Select
- Transformers - OfType and Cast
- Transformers - Metadata
- Bileciki do kontroli - Unit Tests of Interval
- Bileciki do kontroli - Unit Tests of Observer Interval
- Bileciki do kontroli - Unit Tests of Create Cold/Hot Observable
- Szpryca - AutoFac
Wstęp
Dzisiaj poznamy coś nowego, jednak skorzystamy z czegoś starego. Jako inicjatory generowania danych będziemy korzystać jeszcze wielokrotnie z Timer-ów. Bardzo dobrze sprawdzają się jako taki niewolnik, który będzie robił co mu karzemy. “Daj mi tyle i tyle co tyle”.
Observable.Range
Bardzo prostym przykładem generowania danych jest Range. Dlatego nieco go skomplikujemy:). Wygenerujemy prostą tabelką zawierającą informacje o Timestamp-ie, Interval-le i generowanej wartości. Jak można uzyskać taki efekt? Opakowując strumień danych 2x => .TimeInterval().Timestamp(). Skutkuje to otrzymywaniem danych z obiektu obserwowanego w postaci:
- Timestamp,
- Value.Interval,
- Value.Value.
Value to pierwsze opakowanie przy pomocy .TimeInterval().
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Console.WriteLine("RangeGenerator");
Console.WriteLine("Timestamp\t\t\tInterval\t\tValue");
var rangeSource = Observable.Range(start, count)
.TimeInterval()
.Timestamp();
_rangeSubscription = rangeSource.Subscribe(
item =>
{
Console.WriteLine("{0}\t{1}\t{2}",
item.Timestamp,
item.Value.Interval,
item.Value.Value);
},
A co nam daje ten Range? To proste, określamy wartość początkową (start) oraz ilość (count) danych jakie będziemy generować: Observable.Range(start, count).
1
var rangeGenerator = new RangeGenerator(-100, 200);
Prosty przykład:
Dane: start = 0, count = 10
Wynik: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Observable.Generate
Jest i kolejny sposób generowania danych, bardziej wyszukany. Pozwala na kilka ciekawych zabiegów:
- initialState - wartość początkowa,
- condition - warunki jakie możemy określić, jeżeli ich wynikiem będzie true to wówczas będą generowane kolejne dane,
- iterate - jak w pętli for, należy modyfikować podstawę warunków, tak by generowanie mogło się zakończyć, jest to też podstawa generowanych danych,
- resultSelector - wynik iteracji możemy jeszcze zmodyfikować i tutaj można to zrobić, przykład poniżej (Factorial),
- timeSelector - jeszcze trzeba zainicjować proces samodzielnego generowania danych, w tym celu ustalamy co ile czasu będzie wykonywany kolejny etap generowania danych.
Można porównać ten operator do reaktywnej pętli for:).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private static ulong Factorial(ulong i) => i < 1 ? 1 : i * Factorial(i - 1);
public FactorialGenerator()
{
Console.WriteLine("Factorial");
var initialState = 1ul;
var observable = Observable.Generate(
initialState,
condition => condition < 23,
iterate => iterate + 1,
resultSelector => Factorial(resultSelector),
timeSelector => TimeSpan.FromMilliseconds(100)
).TimeInterval();
_subscribeGenerator = observable.Subscribe(
item =>
{
Console.WriteLine("{0} -> {1}", item.Interval, item.Value);
},
Factorial to nic innego jak silnia, zapisana w bardzo skróconej wersji z wykorzystaniem rekurencji.
Z rekurencją trzeba uważać. To żyje własnym życiem! Samo się żywi! Rozmnaża! I czasem nie chce zginąć!
Wyniki generowania przy udziale Silni można zaobserwować oczywiście w przykładzie na GitHub-ie.
1
var generator = new FactorialGenerator();
Zakończenie
Jak widać w powyższych moich wypocinach. Możemy wygenerować interesujące dane asynchronicznie oraz rozesłać je po najdalszych krańcach programu. A z krańców do centrum przy wykorzystaniu innych reaktywnych mechanizmów. Dalej przetworzyć dystrybuować do kolejnych zainteresowanych. Plotkowanie na całego…
Jest to post wchodzący w skład podjętego wyzwania ogłoszonego przez MIROBURN we vlogu z dnia 3 lutego 2018 roku.
Celem wyzwania jest systematyczne działanie w ciągu 30 dni.
Postanowiłem pisać post dziennie o tematyce Programowania Reaktywnego dla platformy .NET.
Wszelkie źródła związane z postami znajdują się na repozytorium GitHub.
Stan obecny wyzwania: 30 z 30 dni.
Referencje:
- MSDN - Getting Started with Rx,
- MSDN - Reactive Extensions,
- 101 Rx Samples,
- ReactiveX,
- Code Project,
- GitHub
Wcześniejszy: Programowanie Reaktywne - Zabawa z czasem - Timestamp/TimeInterval
Następny: Programowanie Reaktywne - Tworzymy dane - Własna klasa publikująca