101 Rx Samples for C# Programming
101 Rx Samples for C# Programming
md 6/28/2019
101 Rx Samples in C#
From [Link]
This was taken from [Link] because I wanted to be able to read it more
comfortable with syntax highlighting.
Yes, you, the one who is still scratching their head trying to figure out this Rx thing. As you learn and
explore, please feel free add your own samples here (or tweak existing ones!) Anyone can (and
should!) edit this page. (edit button is at the bottom right of each page)
(and sorry for the years of spam pages there - they should be gone now. Thanks for marking them. -Rob)
Table of Contents
101 Rx Samples in C
101 Rx Samples - a work in progress
Table of Contents
Asynchronous Background Operations
Start - Run Code Asynchronously
Run a method asynchronously on demand
CombineLatest - Parallel Execution
Create With Disposable & Scheduler - Canceling an asynchronous operation
Observation Operators
Observing an Event - Simple
Observing an Event - Simple (expanded)
Observing MouseMove in Silverlight
Observing an Event - Generic
Observing an Event - Non-Generic
Observing an Asynchronous Operation
Observing a Generic IEnumerable
Observing a Non-Generic IEnumerable - Single Type
Observing a Non-Generic IEnumerable - Multiple Types
Observing the Passing of Time
Restriction Operators
Where - Simple
Where - Drilldown
Projection Operators
Select - Simple
Select - Transformation
1 / 35
[Link] 6/28/2019
Select - Indexed
Grouping
Group By - Simple
Time-Related Operators
Buffer - Simple
Delay - Simple
Interval - Simple
Sample - Simple
Throttle - Simple
Interval - With TimeInterval() - Simple
Interval - With TimeInterval() - Remove
Timeout - Simple
Timer - Simple
Timestamp - Simple
Timestamp - Remove
Window and Joins
Window
GroupJoin - Joins two streams matching by one of their attributes
Range
Range - Prints from 1 to 10.
Generate
Generate - simple
ISubject and ISubject<T1, T2>
Ping Pong Actor Model with ISubject<T1, T2>
Combination Operators
Merge
Publish - Sharing a subscription with multiple Observers
Zip
CombineLatest
Concat - cold observable
Concat - hot observable
Make your class native to IObservable
Use Subject as backend for IObservable
2 / 35
[Link] 6/28/2019
[Link](3000);
[Link]("Background work completed.");
}).Finally(() => [Link]("Main thread completed."));
[Link]("\r\n\t In Main Thread...\r\n");
[Link](); // Wait for completion of background operation.
}
Execute a long-running method asynchronously. The method does not start running until there is a subscriber.
The method is started every time the observable is created and subscribed, so there could be more than one
running at once.
// Synchronous operation
public DataType DoLongRunningOperation(string param)
{
...
}
Merges the specified observable sequences into one observable sequence by emitting a list with the latest
source elements whenever any of the observable sequences produces an element.
3 / 35
[Link] 6/28/2019
Result
Executing 1st on Thread: 3
Executing 2nd on Thread: 4
Executing 3rd on Thread: 3
Done!
Result A
Result B
Result C
Note Was ForkJoin which is no longer supported. CombineLatest gives the same result.)
This sample starts a background operation that generates a sequence of integers until it is canceled by the
main thread. To start the background operation new the Scheduler class is used and a
CancellationTokenSource is indirectly created by a [Link].
Please check out the MSDN documentation on [Link] to learn more
about cancellation source.
IObservable<int> ob =
[Link]<int>(o =>
{
var cancel = new CancellationDisposable(); // internally
creates a new CancellationTokenSource
[Link](() =>
{
int i = 0;
for (; ; )
{
[Link](200); // here we do the long lasting
background operation
if (![Link]) //
check cancel token periodically
[Link](i++);
else
{
[Link]("Aborting because cancel
event was signaled!");
[Link]();
return;
}
}
}
);
return cancel;
}
);
Observation Operators
Observing an Event - Simple
class ObserveEvent_Simple
{
public static event EventHandler SimpleEvent;
static void Main()
{
// To consume SimpleEvent as an IObservable:
var eventAsObservable = [Link](
ev => SimpleEvent += ev,
ev => SimpleEvent -= ev);
}
}
class ObserveEvent_Simple
{
public static event EventHandler SimpleEvent;
5 / 35
[Link] 6/28/2019
[Link]("Subscribe");
//Create two event subscribers
var s = [Link](args =>
[Link]("Received event for s subscriber"));
var t = [Link](args =>
[Link]("Received event for t subscriber"));
[Link]("Raise event");
if (null != SimpleEvent)
{
SimpleEvent(null, [Link]);
}
[Link]("Unsubscribe");
[Link]();
[Link]();
[Link]();
}
}
6 / 35
[Link] 6/28/2019
class ObserveEvent_Generic
{
public class SomeEventArgs : EventArgs { }
public static event EventHandler<SomeEventArgs> GenericEvent;
class ObserveEvent_NonGeneric
{
public class SomeEventArgs : EventArgs { }
public delegate void SomeNonGenericEventHandler(object sender,
SomeEventArgs e);
public static event SomeNonGenericEventHandler NonGenericEvent;
class Observe_IAsync
{
static void Main()
{
// We will use Stream's BeginRead and EndRead for this sample.
Stream inputStream = [Link]();
7 / 35
[Link] 6/28/2019
Be aware that while the code above formally provides an observable, this is not enough for most intended
uses. For more information, see
Creating an observable sequence and
c# - What is the proper way to create an Observable which reads a stream to the end - Stack Overflow.
class Observe_GenericIEnumerable
{
static void Main()
{
IEnumerable<int> someInts = new List<int> { 1, 2, 3, 4, 5 };
class Observe_NonGenericIEnumerableSingleType
{
static void Main()
{
IEnumerable someInts = new object[] { 1, 2, 3, 4, 5 };
8 / 35
[Link] 6/28/2019
class Observe_Time
{
static void Main()
{
// To observe time passing, use the [Link] function.
// It will notify you on a time interval you specify.
Restriction Operators
Where - Simple
class Where_Simple
{
static void Main()
{
var oneNumberPerSecond =
[Link]([Link](1));
[Link](lowNum =>
{
[Link](lowNum);
});
[Link]();
}
}
9 / 35
[Link] 6/28/2019
Result
Numbers < 5:
0 (after 1s)
1 (after 2s)
2 (after 3s)
3 (after 4s)
4 (after 5s)
Where - Drilldown
class Where_DrillDown
{
class Customer
{
public Customer() { Orders = new ObservableCollection<Order>(); }
public string CustomerName { get; set; }
public string Region { get; set; }
public ObservableCollection<Order> Orders { get; private set; }
}
class Order
{
public int OrderId { get; set; }
public DateTimeOffset OrderDate { get; set; }
}
var watchForNewCustomersFromWashington =
from c in customerChanges
where [Link] == [Link]
from cus in [Link]<Customer>
().ToObservable()
where [Link] == "WA"
select cus;
[Link](cus =>
{
[Link]("Customer {0}:", [Link]);
10 / 35
[Link] 6/28/2019
[Link](new Customer
{
CustomerName = "Lazy K Kountry Store",
Region = "WA",
Orders = { new Order { OrderDate = [Link], OrderId
= 1 } }
});
[Link](1000);
[Link](new Customer
{
CustomerName = "Joe's Food Shop",
Region = "NY",
Orders = { new Order { OrderDate = [Link], OrderId
= 2 } }
});
[Link](1000);
[Link](new Customer
{
CustomerName = "Trail's Head Gourmet Provisioners",
Region = "WA",
Orders = { new Order { OrderDate = [Link], OrderId
= 3 } }
});
[Link]();
}
}
Result
New customers from Washington and their orders:
Customer Lazy K Kountry Store: (after 0s)
Order 1: 11/20/2009 11:52:02 AM -06:00
Customer Trail's Head Gourmet Provisioners: (after 2s)
Order 3: 11/20/2009 11:52:04 AM -06:00
Projection Operators
Select - Simple
class Select_Simple
{
11 / 35
[Link] 6/28/2019
[Link]("Numbers * 2:");
[Link](num =>
{
[Link](num);
});
[Link]();
}
}
Result
Numbers * 2:
0 (after 1s)
2 (after 2s)
4 (after 3s)
6 (after 4s)
8 (after 5s)
Select - Transformation
class Select_Transform
{
static void Main()
{
var oneNumberPerSecond =
[Link]([Link](1));
[Link](num =>
{
[Link](num);
});
[Link]();
}
}
12 / 35
[Link] 6/28/2019
Result
Strings from numbers:
(after 0s)
(after 1s)
(after 2s)
(after 3s)
*** (after 4s)
(after 5s)
(after 6s)
Select - Indexed
class Where_Indexed
{
class TimeIndex
{
public TimeIndex(int index, DateTimeOffset time)
{
Index = index;
Time = time;
}
public int Index { get; set; }
public DateTimeOffset Time { get; set; }
}
[Link](timeIndex =>
{
[Link](
"Ding dong. The time is now {0:T}. This is event number
{1}.",
[Link],
[Link]);
});
[Link]();
}
}
Result
Ding dong. The time is now 1:55:00 PM. This is event number 0. (after 0s)
Ding dong. The time is now 1:55:01 PM. This is event number 1. (after 1s)
Ding dong. The time is now 1:55:02 PM. This is event number 2. (after 2s)
Ding dong. The time is now 1:55:03 PM. This is event number 3. (after 3s)
13 / 35
[Link] 6/28/2019
Ding dong. The time is now 1:55:04 PM. This is event number 4. (after 4s)
Ding dong. The time is now 1:55:05 PM. This is event number 5. (after 5s)
Grouping
Group By - Simple
This example counts how many time you press each key as you furiously hit the keyboard. :)
class GroupBy_Simple
{
static IEnumerable<ConsoleKeyInfo> KeyPresses()
{
for (; ; )
{
var currentKey = [Link](true);
if ([Link] == [Link])
yield break;
else
yield return currentKey;
}
}
static void Main()
{
var timeToStop = new ManualResetEvent(false);
var keyPresses = KeyPresses().ToObservable();
var groupedKeyPresses =
from k in keyPresses
group k by [Link] into keyPressGroup
select keyPressGroup;
[Link](keyPressGroup =>
{
int numberPresses = 0;
[Link](keyPress =>
{
[Link](
"You pressed the {0} key {1} time(s)!",
[Link],
++numberPresses);
},
() => [Link]());
});
[Link]();
14 / 35
[Link] 6/28/2019
}
}
Result
Depends on what you press! But something like:
Press Enter to stop. Now bang that keyboard!
You pressed the A key 1 time(s)!
You pressed the A key 2 time(s)!
You pressed the B key 1 time(s)!
You pressed the B key 2 time(s)!
You pressed the C key 1 time(s)!
You pressed the C key 2 time(s)!
You pressed the C key 3 time(s)!
You pressed the A key 3 time(s)!
You pressed the B key 3 time(s)!
You pressed the A key 4 time(s)!
You pressed the A key 5 time(s)!
You pressed the C key 4 time(s)!
Time-Related Operators
Buffer - Simple
class Buffer_Simple
{
static IEnumerable<string> EndlessBarrageOfEmail()
{
var random = new Random();
var emails = new List<String> { "Here is an email!", "Another
email!", "Yet another email!" };
for (; ; )
{
// Return some random emails at random intervals.
yield return emails[[Link]([Link])];
[Link]([Link](1000));
}
}
static void Main()
{
var myInbox = EndlessBarrageOfEmail().ToObservable();
[Link](emails =>
{
[Link]("You've got {0} new messages! Here they
are!", [Link]());
foreach (var email in emails)
{
[Link]("> {0}", email);
}
[Link]();
});
[Link]();
}
}
Result
You've got 5 new messages! Here they are! (after 3s)
> Here is an email!
> Another email!
> Here is an email!
> Another email!
> Here is an email!
Delay - Simple
class Delay_Simple
{
static void Main()
{
var oneNumberEveryFiveSeconds =
[Link]([Link](5));
// Instant echo
[Link](num =>
{
[Link](num);
});
16 / 35
[Link] 6/28/2019
[Link]([Link](1)).Subscribe(num =>
{
[Link]("...{0}...", num);
});
[Link]([Link](2)).Subscribe(num =>
{
[Link]("......{0}......", num);
});
[Link]();
}
}
Result
0 (after 5s)
…0… (after 6s)
……0…… (after 7s)
1 (after 10s)
…1… (after 11s)
……1…… (after 12s)
Interval - Simple
using ([Link]([Link]))
{
[Link]("Press any key to unsubscribe");
[Link]();
}
Result
0 (after 1s)
1 (after 2s)
2 (after 3s)
17 / 35
[Link] 6/28/2019
3 (after 4s)
…
Sample - Simple
Result
15: 24/11/2009 15:40:45 (after 1s)
31: 24/11/2009 15:40:46 (after 2s)
47: 24/11/2009 15:40:47 (after 3s)
64: 24/11/2009 15:40:48 (after 4s)
…
Throttle - Simple
Throttle stops the flow of events until no more events are produced for a specified period of time. For
example, if you throttle a TextChanged event of a textbox to .5 seconds, no events will be passed until the user
has stopped typing for .5 seconds. This is useful in search boxes where you do not want to start a new search
after every keystroke, but want to wait until the user pauses.
SearchTextChangedObservable =
[Link]<TextChangedEventArgs>([Link],
"TextChanged");
_currentSubscription =
[Link]([Link](.5)).ObserveOnDi
spatcher().Subscribe(e => [Link]([Link]));
18 / 35
[Link] 6/28/2019
while(true)
{
if(i > 1000)
{
yield break;
}
yield return i;
[Link]( i++ % 10 < 5 ? 500 : 1000);
}
}
Result
5:
6:
7:
8:
9:
15:
16:
17:
19 / 35
[Link] 6/28/2019
18:
19:
…etc
using ([Link](
x => [Link]("{0}: {1}", [Link], [Link])))
{
[Link]("Press any key to unsubscribe");
[Link]();
}
Result
0: 00:00:00.8090459 (1st value)
1: 00:00:00.7610435 (2nd value)
2: 00:00:00.7650438 (3rd value)
…
// Remove it again
using
([Link]().Subscribe([Link]))
{
[Link]("Press any key to unsubscribe");
[Link]();
20 / 35
[Link] 6/28/2019
Result
0
1
2
…
Timeout - Simple
using ([Link](
x => [Link]("{0}: {1}", [Link], [Link]),
ex => [Link]("{0} {1}", [Link], [Link])))
{
[Link]("Press any key to unsubscribe");
[Link]();
}
Result
02/12/2009 10:13:00
Press any key to unsubscribe
The operation has timed out. 02/12/2009 10:13:09
…
Timer - Simple
21 / 35
[Link] 6/28/2019
[Link](1)).Timestamp();
// or, equivalently
// var observable = [Link]([Link] +
[Link](5),
//
[Link](1)).Timestamp();
using ([Link](
x => [Link]("{0}: {1}", [Link], [Link])))
{
[Link]("Press any key to unsubscribe");
[Link]();
}
Result
02/12/2009 10:02:29
Press any key to unsubscribe
0: 02/12/2009 10:02:34(after 5s)
1: 02/12/2009 10:02:35 (after 6s)
2: 02/12/2009 10:02:36 (after 7s)
…
Timestamp - Simple
22 / 35
[Link] 6/28/2019
using ([Link](
x => [Link]("{0}: {1}", [Link], [Link])))
{
[Link]("Press any key to unsubscribe");
[Link]();
}
Result
0: 24/11/2009 15:40:45 (after 1s)
1: 24/11/2009 15:40:46 (after 2s)
2: 24/11/2009 15:40:47 (after 3s)
3: 24/11/2009 15:40:48 (after 4s)
…
Timestamp - Remove
// Remove it
using ([Link]().Subscribe([Link]))
{
[Link]("Press any key to unsubscribe");
[Link]();
}
Result
0 (after 1s)
1 (after 2s)
2 (after 3s)
3 (after 4s)
…
Window
Divides a stream into "Windows" of time. For example, 5 five second window would contain all elements
pushed in that five second interval.
IObservable<long> mainSequence =
[Link]([Link](1));
IObservable<IObservable<long>> seqWindowed = [Link](() =>
{
IObservable<long> seqWindowControl =
[Link]([Link](6));
return seqWindowControl;
});
[Link](seqWindow =>
{
[Link]("\nA new window into the main sequence has
opened: {0}\n",
[Link]());
[Link](x => { [Link]("Integer : {0}", x);
});
});
[Link]();
var l = [Link]();
var r = [Link]();
var q = [Link](r,
_ => [Link]<Unit>(), // windows from each left event going
on forever
_ => [Link]<Unit>(), // windows from each right event going
on forever
(left, obsOfRight) => [Link](left, obsOfRight)); // create tuple
of left event with observable of right events
{
var xs = e.Item2;
[Link](
x => x[0] == e.Item1[0]) // filter only when datetime matches
.Subscribe(
v =>
{
[Link](
[Link]("{0},{1} and {2},{3} occur at the same time",
e.Item1[0],
e.Item1[1],
v[0],
v[1]
));
});
});
Range
Generates a Range of values. Useful for testing purposes.
Generate
There are several overloads for Generate.
Generate - simple
x=>[Link](1)).Timestamp();
25 / 35
[Link] 6/28/2019
Result
1: 24/11/2009 15:40:45 (after 1s)
2: 24/11/2009 15:40:46 (after 2s)
3: 24/11/2009 15:40:47 (after 3s)
4: 24/11/2009 15:40:48 (after 4s)
5: 24/11/2009 15:40:49 (after 5s)
using System;
using [Link];
using [Link];
namespace RxPingPong
{
/// <summary>Simple Ping Pong Actor model using Rx </summary>
/// <remarks>
/// You'll need to install the Reactive Extensions (Rx) for this to
work.
/// You can get the installer from <see
href="[Link]
/// </remarks>
class Program
{
static void Main(string[] args)
{
var ping = new Ping();
var pong = new Pong();
26 / 35
[Link] 6/28/2019
[Link]();
[Link]();
[Link]();
/// <summary>
/// Notifies the observer of a new value in the sequence.
/// </summary>
public void OnNext(Pong value)
{
[Link]("Ping received Pong.");
}
/// <summary>
/// Notifies the observer that an exception has occurred.
/// </summary>
public void OnError(Exception exception)
{
[Link]("Ping experienced an exception and had to
quit playing.");
}
/// <summary>
/// Notifies the observer of the end of the sequence.
/// </summary>
public void OnCompleted()
{
[Link]("Ping finished.");
}
#endregion
/// <summary>
/// Subscribes an observer to the observable sequence.
/// </summary>
public IDisposable Subscribe(IObserver<Ping> observer)
{
return [Link]([Link](2))
.Where(n => n < 10)
.Select(n => this)
.Subscribe(observer);
}
#endregion
27 / 35
[Link] 6/28/2019
/// <summary>
/// Performs application-defined tasks associated with freeing,
releasing, or resetting unmanaged resources.
/// </summary>
/// <filterpriority>2</filterpriority>
public void Dispose()
{
OnCompleted();
}
#endregion
}
/// <summary>
/// Notifies the observer of a new value in the sequence.
/// </summary>
public void OnNext(Ping value)
{
[Link]("Pong received Ping.");
}
/// <summary>
/// Notifies the observer that an exception has occurred.
/// </summary>
public void OnError(Exception exception)
{
[Link]("Pong experienced an exception and had to
quit playing.");
}
/// <summary>
/// Notifies the observer of the end of the sequence.
/// </summary>
public void OnCompleted()
{
[Link]("Pong finished.");
}
#endregion
/// <summary>
/// Subscribes an observer to the observable sequence.
/// </summary>
public IDisposable Subscribe(IObserver<Pong> observer)
{
28 / 35
[Link] 6/28/2019
return [Link]([Link](1.5))
.Where(n => n < 10)
.Select(n => this)
.Subscribe(observer);
}
#endregion
/// <summary>
/// Performs application-defined tasks associated with freeing,
releasing, or resetting unmanaged resources.
/// </summary>
/// <filterpriority>2</filterpriority>
public void Dispose()
{
OnCompleted();
}
#endregion
}
}
Result
1: Ping received Pong.
2: Pong received Ping.
3: Ping received Pong.
4: Pong received Ping.
5: Ping received Pong.
Combination Operators
Merge
The Merge operator combine two or more sequences. In the following example, the two streams are merged
into one so that both are printed with one subscription. Also note the use of "using" to wrap the Observable,
thus ensuring the subscription is Disposed.
class Merge
{
private static IObservable<int> Xs
{
get { return Generate(0, new List<int> {1, 2, 2, 2, 2}); }
}
29 / 35
[Link] 6/28/2019
return [Link](initialValue,
x => x < initialValue + [Link]
- 1,
x => x + 1,
x => x,
x => [Link](intervals[x -
initialValue]));
}
using ([Link](Ys).Timestamp().Subscribe(
z => [Link]("{0,3}: {1}", [Link], [Link]),
() => [Link]("Completed, press a key")))
{
[Link]();
}
result
0: 11/12/2009 12:17:44
100: 11/12/2009 12:17:45
1: 11/12/2009 12:17:46
101: 11/12/2009 12:17:47
2: 11/12/2009 12:17:48
102: 11/12/2009 12:17:49
3: 11/12/2009 12:17:50
103: 11/12/2009 12:17:51
4: 11/12/2009 12:17:52
104: 11/12/2009 12:17:53
class Publish
{
private static void Main()
30 / 35
[Link] 6/28/2019
{
var unshared = [Link](1, 4);
[Link]();
result
Unshared Subscription #1: 1
Unshared Subscription #1: 2
Unshared Subscription #1: 3
Unshared Subscription #1: 4
Unshared Subscription #2: 1
Unshared Subscription #2: 2
Unshared Subscription #2: 3
Unshared Subscription #2: 4
Zip
class Zip
{
// same code as above for Merge...
31 / 35
[Link] 6/28/2019
result
100: 11/12/2009 12:17:45
102: 11/12/2009 12:17:47
104: 11/12/2009 12:17:49
106: 11/12/2009 12:17:51
108: 11/12/2009 12:17:53
CombineLatest
class CombineLatest
{
// same code as above for Merge...
result
100: 11/12/2009 12:17:45
32 / 35
[Link] 6/28/2019
101: 11/12/2009 12:17:46
102: 11/12/2009 12:17:47
103: 11/12/2009 12:17:48
104: 11/12/2009 12:17:49
105: 11/12/2009 12:17:50
106: 11/12/2009 12:17:51
107: 11/12/2009 12:17:52
108: 11/12/2009 12:17:53
class ConcatCold
{
private static IObservable<int> Xs
{
get { return Generate(0, new List<int> {0, 1, 1}); }
}
[Link]([Link]);
using ([Link](Ys).Timestamp().Subscribe(
z => [Link]("{0,3}: {1}", [Link], [Link]),
() => [Link]("Completed, press a key")))
{
[Link]();
}
result
0: 11/12/2009 12:17:45
1: 11/12/2009 12:17:46
2: 11/12/2009 12:17:47
100: 11/12/2009 12:17:48
33 / 35
[Link] 6/28/2019
101: 11/12/2009 12:17:49
102: 11/12/2009 12:17:50
class ConcatHot
{
private static IObservable<int> Xs
{
get { return Generate(0, new List<int> {0, 1, 1}); }
}
[Link]([Link]);
using ([Link](Ys).Timestamp().Subscribe(
z => [Link]("{0,3}: {1}", [Link], [Link]),
() => [Link]("Completed, press a key")))
{
[Link]();
}
result
0: 11/12/2009 12:17:45
1: 11/12/2009 12:17:46
2: 11/12/2009 12:17:47
102: 11/12/2009 12:17:48
34 / 35
[Link] 6/28/2019
class UseSubject
{
public class Order
{
private DateTime? _paidDate;
[Link]([Link]);
}
}
35 / 35