Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rx - can/should I replace .NET events with Observables?

Given the benefits of composable events as offered by the Reactive Extensions (Rx) framework, I'm wondering whether my classes should stop pushing .NET events, and instead expose Rx observables.

For instance, take the following class using standard .NET events:

public class Foo {    private int progress;    public event EventHandler ProgressChanged;     public int Progress    {       get { return this.progress; }       set       {          if (this.progress != value)          {             this.progress = value;              // Raise the event while checking for no subscribers and preventing unsubscription race condition.             var progressChanged = this.ProgressChanged;             if (progressChanged != null)             {                 progressChanged(this, new EventArgs());             }          }       }    } } 

Lot of monotonous plumbing.

This class could instead use some sort of observable to replace this functionality:

public class Foo {    public Foo()    {        this.Progress = some new observable;    }     public IObservable<int> Progress { get; private set; } } 

Far less plumbing. Intention is no longer obscured by plumbing details. This seems beneficial.

My questions for you fine StackOverflow folks are:

  1. Would it good/worthwhile to replace standard .NET events with IObservable<T> values?
  2. If I were to use an observable, what kind would I use here? Obviously I need to push values to it (e.g. Progress.UpdateValue(...) or something).
like image 317
Judah Gabriel Himango Avatar asked Aug 25 '10 16:08

Judah Gabriel Himango


2 Answers

For #2, the most straightforward way is via a Subject:

Subject<int> _Progress; IObservable<int> Progress {     get { return _Progress; } }  private void setProgress(int new_value) {     _Progress.OnNext(new_value); }  private void wereDoneWithWorking() {     _Progress.OnCompleted(); }  private void somethingBadHappened(Exception ex) {     _Progress.OnError(ex); } 

With this, now your "Progress" can not only notify when the progress has changed, but when the operation has completed, and whether it was successful. Keep in mind though, that once an IObservable has completed either via OnCompleted or OnError, it's "dead" - you can't post anything further to it.

like image 62
Ana Betts Avatar answered Oct 02 '22 15:10

Ana Betts


I don't recommend managing your own subscriber list when there are built in subjects that can do that for you. It also removes the need for carrying your own mutable copy of T.

Below is my (commentless) version of your solution:

public class Observable<T> : IObservable<T>, INotifyPropertyChanged  {      private readonly BehaviorSubject<T> values;       private PropertyChangedEventHandler propertyChanged;       public Observable() : this(default(T))     {     }       public Observable(T initalValue)      {          this.values = new BehaviorSubject<T>(initalValue);          values.DistinctUntilChanged().Subscribe(FirePropertyChanged);     }      public T Value      {          get { return this.values.First(); }          set { values.OnNext(value); }      }      private void FirePropertyChanged(T value)     {         var handler = this.propertyChanged;          if (handler != null)             handler(this, new PropertyChangedEventArgs("Value"));     }      public override string ToString()      {          return value != null ? value.ToString() : "Observable<" + typeof(T).Name + "> with null value.";      }       public static implicit operator T(Observable<T> input)      {          return input.Value;      }       public IDisposable Subscribe(IObserver<T> observer)      {          return values.Subscribe(observer);     }       event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged      {          add { this.propertyChanged += value; }          remove { this.propertyChanged -= value; }      }  } 
like image 41
Richard Szalay Avatar answered Oct 02 '22 16:10

Richard Szalay