4

I'm implementing the IObservable<T> interface on some classes. I used Reflector to figure out how this is typically done in Rx. Concerning how an observable keeps track of its subscribers and notifies them via their OnNext method, I stumbled upon code similar to this:

private List<Observer<T>> observers;

// subscribe a new observer:
public IDisposable Subscribe(IObserver<T> observer)
{
    observers.Add(observer);
    ...
}

// trigger all observers' OnNext method:
...
foreach (IObserver<T> observer in observers)
{
    observer.OnNext(value);
}

Since all delegates are multi-cast, couldn't this be simplified to:

Action<T> observers;

// subscribe observer:
public IDisposable Subscribe(IObserver<T> observer)
{
    observers += observer.OnNext;
    ...
}

// trigger observers' OnNext:
...
observers(value);

Or are there specific advantages to the first approach (performance, threading/concurrency issues, …)?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268

2 Answers2

5

In general, calling the delegates individually gives you more control over the behavior:

  • If one delegate raises an exception you can keep calling the others, for example, or remove the faulted delegate from your list.
  • If you want to call the delegates in parallel, it's really easy.
  • If you need to call them in a certain order, you can easily guarantee the correct order (I'm not sure that the order of multicast delegate calls is defined).
Gabe
  • 84,912
  • 12
  • 139
  • 238
  • In addition, you would also run into problems with duplicates if you used the multicast delegate approach. If two observers with the exact same OnNext methods were added, and one then removed, that would remove the callback for both of them. – SoftMemes Oct 23 '10 at 20:17
  • Freed: No, that's not a problem. Only the first instance of a given delegate is removed. – Gabe Oct 23 '10 at 21:25
  • You're right. What will happen however is that the "wrong" delegate will be removed which would change the order the observers are notified (which may or may not be relevant). – SoftMemes Oct 23 '10 at 21:59
  • The problem with the benefits you describe is that utilising any of them would make your observable act differently than the rest of Rx. – Richard Szalay Oct 25 '10 at 12:10
  • @Richard, what are you referring to? What kind of assumptions does Rx make about `IObservable` implementations? I previously thought that an implementation could notify its observers in whatever way it liked, as long as it *does* notify them all...? – stakx - no longer contributing Sep 27 '11 at 07:41
  • @stakx - Wow, flashback question! `IObservable` itself has no expectations, but Rx has conventions. For example, calling subsequent observers after an error might be ok for the `IObservable` contract, but in Rx it seems out of place so it'd be something you'd want to make explicit (in naming or documentation). – Richard Szalay Sep 27 '11 at 08:23
5

Usually you don't implement IObservable<T> yourself, you return an IObservable<T> from a method using one of the generation methods (like Observable.Create).

However, if you are going to implement the interface yourself, you should wrap an internal Subject<T> which will handle all the concurrency issues for you:

public class CustomObservable<T> : IObservable<T>
{
    private Subject<T> subject = new Subject<T>();

    public IDisposable Subscribe(IObserver<T> observer)
    {
        return subject.Subscribe(observer);
    }

    private void EmitValue(T value)
    {
        subject.OnNext(value);
    }
}

NB: If you decide to stick with the delegate (for whatever reason), at least make sure you are unsubscribing in your IDisposable return value:

observers += observer.OnNext;
return Disposable.Create(() => observers -= observer.OnNext);
Richard Szalay
  • 83,269
  • 19
  • 178
  • 237
  • thanks for answering. Note however that my library is not based on Rx, I am relying only on the two interfaces in the .NET 4.0 BCL. I only checked with the Rx implementation to see how it's usually done. – stakx - no longer contributing Oct 25 '10 at 14:49
  • 3
    Yes, this is great advice! Think of implementing IObservable directly as common as implementing IEnumerable; usually you'll try to use one of the already-built IObservable implementations like Subject – Ana Betts Oct 26 '10 at 00:33