Reactive Extensions for .Net provides developers a set of functionalities to implement reactive programming models for .Net developers making event handling simpler and expressive using declarative operations. While the key cornerstones of reactive extensions are the IObserver and IObservable interfaces, it is not often you need to implement the interfaces yourself as a developer. The library supports an inbuilt type Subject<T> which implements both the interfaces and supports a host of functions.
Subject is the basic of the different available subjects in the library, there are others too - ReplaySubject<T>, BehaviorSubject<T> and AsyncSubject<T>. It is quite useful to understand the essential differences between them and how you could use them to make better use of the library.
In this article, we will compare the Subject<T> and its siblings in an attempt to illustrate the differences between their behaviors.
Subject<T>
As mentioned earlier, Subject<T> is the basic of the available subjects and it provides an easy way to work with the library without having to implement the IObservable<T> and IObserver<T> interfaces yourself. A simple demo of the Subject type is shown below.
var subject = new Subject<int>();
subject.Subscribe(Console.Write);
subject.OnNext(1);
subject.OnNext(2);
subject.OnNext(3);
subject.OnNext(4);
In the code above, we have created an instance of Subject<T> and since it implements both IObserver<T> and IObserverable<T>, have used the same instance for Subscribe and publishing values to the IObserver. Another important point to note here is how we have used an overload of Subscribe method which accepts an action as input. The action would be executed for each value published, which in this case is to print the number to console.
Let us attempt to display the published values and values printed into the console by the IObserver (in this Action<T>) in the following image. This would help us compare the remaining siblings and variations easily.

The first line indicates the published values, while the second line indicates values received by the IObserver. Also, we have added a line to indicate at what point of execution Observer subscribes to the stream of data. This line is indicated by the dotted vertical line.
In the code above, we notice that the observer subscribes to the stream of data before the first value is published. This is illustrated in the image as the subscriber line is placed before the first element. As seen in the output line, this makes no difference (at this point) to the output.
But what if the Observer subscribes to the data only after some of the values are already published? Would that make a difference to the data received by the Observer? Let us first write the code for the same before looking at the output.
var subject = new Subject<int>();
subject.OnNext(1);
subject.OnNext(2);
subject.Subscribe(Console.Write);
subject.OnNext(3);
subject.OnNext(4);
In the code shown above, one can observe that the Observer subscribes to the stream of data only after two values (1 and 2) are published. As one would expect, this would result in the Observer not receiving the data which were published prior to the call to subscribe method. This is illustrated in the image below.

What if you wanted to read all the published values even if the Observer had subscribed late. This is where ReplaySubject<T> comes into the party.
ReplaySubject<T>
ReplaySubject<T> caches the values and replays them for late subscribers. This is useful in avoiding race conditions. Let us change the previous code to use ReplaySubject<T> and see how it impacts what the observer receives.
var subject = new ReplaySubject<int>();
subject.OnNext(1);
subject.OnNext(2);
subject.Subscribe(Console.Write);
subject.OnNext(3);
subject.OnNext(4);
As seen in the code above, there is almost no change in the code, except the fact that we are now using ReplaySubject<T> instead of Subject<T>. The following diagram illustrates the impact on the data received by the Observer.

As illustrated, the cached values are now replayed to the subscriber even if it subscribes late. This useful functionality of course at its own cost. This implementation would cache each and every value published by the subscriber, which could lead to undesirable memory issues when the volume of data is significantly larger.
However, ReplaySubject<T> has solutions for the problem in more than one way. For sake of this example, we will look into two examples, which would use Size and Time Constraints to limit the cached values.
As the first case, we will limit the cached values using the size of the cache. The constructor of ReplaySubject<T> provides an overload that accepts an integer which signifies the size (maximum element count) of the cache buffer. Let us change the code to limit the cache size to 1 in our example.
var subject = new ReplaySubject<int>(1);
subject.OnNext(1);
subject.OnNext(2);
subject.Subscribe(Console.Write);
subject.OnNext(3);
subject.OnNext(4);
Notice how we have supplied the size of Cache as 1 using the constructor overload of ReplaySubject<T>. This restricts the cache and ensures only one element is cached and would be replaced with the new element as soon as it is published. The impact of the change is illustrated below.








Shervin CyrilPosted Dec 30, 2021, 11:53 AM
Good one! Thanks for sharing !
Sagar LadPosted Oct 8, 2021, 2:47 PM
Nice Article, Thanks for sharing