Rx.Net.Plus
Smart Reactive Extensions
Install / Use
/learn @ReactiveSoftware/Rx.Net.PlusREADME
Rx.Net.Plus
Table of content
Introduction
ReactiveX gains popularity and is widely used in several platforms and languages.
Following is an excerpt of Rx.Net Brief Intro :
"The Reactive Extensions (Rx) is a library for composing asynchronous and event-based programs using observable sequences and LINQ-style query operators. Using Rx, developers represent asynchronous data streams with Observables, query asynchronous data streams using LINQ operators, and parameterize the concurrency in the asynchronous data streams using Schedulers.
Simply put, Rx = Observables + LINQ + Schedulers."
If you have used Rx.Net (System.Reactive), you probably already wished to turn regular variable (like int, bool ) to become reactive.
What do we mean? We would like to:
- Be notified asynchronously whenever the value is changed (push model vs pull model)
- Connect variables to observable sources (be an observer) and utilize LINQ semantics
- Avoid repetitive notification of same value (as it doesn't matter with regular variables)
- Be able to synchronously get value of reactive variables using regular c# variable semantics. Compare them to other variables\constants, convert them to another type...
Hence, we could state that Rx.Net.Plus will lead to the following formula:
Rx.Net.Plus = Rx.Net + Observers + classic (pull model) c# semantics.
Rx.Net.Plus is a small but smart library which introduces two new types for this purpose (+ several extension methods):
| Type | Purpose | | :--------------- | ------------------------------------------------------------ | | RxVar | supersede basic types (as int, bool...) | | RxProperty | Support of MVVM to replace view-model properties (NotifyPropertyChanged pattern) |
RxVar
Basics
Let's start with an example:
Suppose you have in your code, some status of a connection named isConnected.
It may be defined as following:
bool IsConnected { get; set; }
You may want to react to change of connection status. One way is to poll this status periodically.
ReactiveX introduces the push model extending the Observer pattern.
One have to define an observable and update its state.
Other have to listen to events related to this observable as following:
IObservable<bool> IsConnected { get; set; }
....
IsConnected.Subscribe (_isConnected => _isConnected ? doSomething() : doAnotherThing());
Some questions arrive:
- How do we create this observable in a simple and straightforward way?
- How do we update the state of the observable?
- How can we use it as an ordinary variable - by polling its values -
if (IsConnected) doSomething()? - How can we chain information from another source to update the IsConnected status?
The answer is: Rx.Net.Plus !!
With Rx.Net.Plus, it is super simple to define this kind of variable.
var isConnected = false.ToRx(); // Create a reactive variable
We can easily update its state.
isConnected.Value = true;
Suppose we have a <u>sequential</u> algorithm, no problem to write this kind of code:
if (isConnected) // Note we don't use .Value of isConnected
{
StartWork();
}
else
{
StopWork();
}
Let's say, connectionStatus is of type Observable
IObservable<bool> connectionStatus;
we can write:
connectionStatus.Subscribe (isConnected);
or using Rx.Net.Plus semantics:
isConnected.ListenTo (connectionStatus);
or
connectionStatus.RedirectTo (isConnected);
or
connectionStatus.Notify (isConnected);
Rx.Net.Plus provides full comparison and equality support:
var anotherRxBool = true.ToRx();
if (isConnected == anotherRxBool)
{
// Do some action
}
With primitive types (int, double...)
var intRx = 10.ToRx(); // Create a rxvar
if (intRx > 5)
{
// React !!
}
RxVar could be used as part of state machines, and instead of polling statuses, flags, counters...
Just react upon change in this manner:
isConnected.When(true).Notify (v => reactToEvent());
It could be used for storing application information:
class SystemInfo
{
RxVar<DateTime> CurrentTime;
RxVar<bool> AreHeadphonesConnected;
}
...
systemInfo.CurrentTime.Notify (dt => DisplayToScreen (dt)); // Reactive !
....
DateTime deadlineDate;
if (systemInfo.CurrentTime > deadlineDate) // Classic polling !
{
SendNotification();
}
Note that RxVar behaves exactly as a classic variable. Therefore, as for classic variable, in case it is not initialized the value will be the default, RxVar will also publish the default value on subscription.
One option is to use the IgnoreNull* extension to avoid receiving the null value, or use Skip(1) to skip first value.
RxVar implements the following interfaces:
ISubject, IConvertible, IDisposable, IComparable, IEquatable, ISerializable
Comparison
By default, RxVar uses for its comparisons the default comparer of the generic type used
Comparer<T>.Default
In case a specific comparer is needed, RxVar provides the SetComparer method to specify it.
Boolean Operators
RxVar has the following 'pseudo' boolean operators: True, False, Not for comparison
if (isConnected.Not) equivalent if (! isConnected)
Distinct mode
By default, RxVar propagates its data (on new value assignment) only when a new distinct value is set.
That means that if some observer is subscribed to RxVar and the same value is assigned to RxVar, it will not be published.
In order to allow every update to be propagated, RxVar provide the following property:
IsDistinctMode
Note: distinct mode may be changed at any time even after subscription of observers.
ReadOnlyRxVar
RxVar can be published as read-only, similarly to IReadOnlyList concept for arrays.
This allows using of RxVar to be used without caring of being modified outside.
var rxVar = 10.ToRxVar();
var roRxVar = (IReadOnlyRxVar<int>)rxVar;
roRxVar.Value = 30; // Won't compile !!!
rxVar.Value = 20; // Compile
var val = roRxVar.Value; // val is equal to 20
Rx.Net.Plus also offers a ToReadOnlyRxVar (similar to ToReadOnlyList method) method to convert a rw RxVar to a read-only RxVar which allows regular variable syntax:
var rxVar = 10.ToRxVar();
var roRxVar = rxVar.ToReadOnlyRxVar();
Assert.True(roRxVar > 5); // This syntax is unavailable when using IReadOnlyRxVar<int> casting
Disposing
Rx.Net.Plus provides a base class which implements IDisposable, called DisposableBaseClass.
DisposableBaseClass has a built-in CancellationToken used by RxVar and RxProperty to automatically unsubscribe (in case they are used as observers) on disposing.
This is very convenient as there no need to handle IDisposable...when subscribing RxVar to observables.
The following method utilizes automatic un-subscription
ListenTo(IObservable<T> observable)
// Classic style
IDisposable subscription = observable.Subscribe (rxVar);
subscription.Dispose();
// Thanks to DisposabeBaseClass (used by RxVar)
rxVar.ListenTo (observable);
....
// RxVar is automatically unsubscribed from observable when disposed
Serialization
RxVar supports standard serialization (have been tested for binary, xml, Json formats).
The following fields are serialized: Value, IsDistinct.
Json Flat Serialization
What is Json "flat" serialization?
let's give an example. The following is part of the Test module in the solution.
public class Info
{
public string Title { get; set; } = "Mr";
public double Height { get; set; } = 76.5;
}
public class User
{
public RxVar<string> Name = "John".ToRxVar();
public RxVar<bool> IsMale = true.ToRxVar();
public RxVar<int> Age = 16.ToRxVar();
public RxVar<Info> InfoUser = new Info().ToRxVar();
public User()
{
Name.IsDistinctMode = false;
}
}
Normal Json serialization will output:
{
"Name": {
"IsDistinctMode": false,
"Value": "John"
},
"IsMale": {
"IsDistinctMode": true,
"Value": true
},
"Age": {
"IsDistinctMode": true,
"Value": 16
},
"InfoUser": {
"IsDistinctMode": true,
"Value": {
"Title": "Mr",
"Height": 76.5
}
}
With flat serialization:
{
"Name": "John",
"IsMale": true,
"Age": 16,
"InfoUser": {
"Title": "Mr",
"Height": 76.5
}
}
Json flat serialization is available through a dedicated Nuget package.
In order to apply flat serialization, follow these steps:
-
Add a reference to Rx.Net.Plus.Json package.
-
Call once the following method to apply this serialization style:
RxVarFlatJsonExtensions.RegisterToJso
Related Skills
node-connect
333.3kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
82.0kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
333.3kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
82.0kCommit, push, and open a PR
