Search code examples
c#design-patterns.net-4.5system.reactive

IObserver and IObservable in C# for Observer vs Delegates, Events


All I am trying to do is implementing the observer pattern.

So, I came up with this solution:

We have a PoliceHeadQuarters whose primary job is to send notifications to all those who are subscribed to it. Consider that the DSP, Inspector and SubInspector classes are subscribed to PoliceHeadQuarters.

Using Events and Delegates I wrote

public class HeadQuarters 
{
    public delegate void NewDelegate(object sender, EventArgs e);
    public event EventHandler NewEvent;
    public void RaiseANotification()
    {
        var handler = this.NewEvent;
        if (handler != null)
        {
            handler(this, new EventArgs());
        }
    }
}

public class SubInspector
{
    public void Listen(object sender, EventArgs e)
    {
        MessageBox.Show(string.Format("Event Notification received by sender = {0} with eventArguments = {1}", sender, e.ToString()));
    }
}

public class Inspector
{
    public void Listen(object sender, EventArgs e)
    {
        MessageBox.Show(string.Format("Event Notification received by sender = {0} with eventArguments = {1}", sender, e.ToString()));
    }
}

and this is how I invoked it

       var headQuarters = new HeadQuarters();
        var SubInspector = new SubInspector();
        var Inspector = new Inspector();
        headQuarters.NewEvent += Inspector.Listen;
        headQuarters.NewEvent += SubInspector.Listen;
        headQuarters.RaiseANotification();

so, both Inspector and SubInspector classes get notification whenever there the function RaiseANotification() is invoked.

It seems that the DotNet Framework 4, 4.5 supports a new way called IObserver and IObservable.

Can anyone give me a super simple example using IObservable and IObserver pattern for the above scenario? I googled only to find the available examples in the internet too bloated and difficult to understand.

My hinch: (probably i think it's wrong)

  class DSP : IObserver //since it observes the headquarters ?
  class PoliceHeadQuarters: IObservable // since here's where we send the notifications ?

Thanks in advance.

EDIT: Somebody also said that the MSDN documentation is also incorrect for IObservable @ IObservable vs Plain Events or Why Should I use IObservable?.


Solution

  • Here's a modification of MSDN example to fit your framework:

        public struct Message
        {
            string text;
    
            public Message(string newText)
            {
                this.text = newText;
            }
    
            public string Text
            {
                get
                {
                    return this.text;
                }
            }
        }
    
        public class Headquarters : IObservable<Message>
        {
            public Headquarters()
            {
                observers = new List<IObserver<Message>>();
            }
    
            private List<IObserver<Message>> observers;
    
            public IDisposable Subscribe(IObserver<Message> observer)
            {
                if (!observers.Contains(observer))
                    observers.Add(observer);
                return new Unsubscriber(observers, observer);
            }
    
            private class Unsubscriber : IDisposable
            {
                private List<IObserver<Message>> _observers;
                private IObserver<Message> _observer;
    
                public Unsubscriber(List<IObserver<Message>> observers, IObserver<Message> observer)
                {
                    this._observers = observers;
                    this._observer = observer;
                }
    
                public void Dispose()
                {
                    if (_observer != null && _observers.Contains(_observer))
                        _observers.Remove(_observer);
                }
            }
    
            public void SendMessage(Nullable<Message> loc)
            {
                foreach (var observer in observers)
                {
                    if (!loc.HasValue)
                        observer.OnError(new MessageUnknownException());
                    else
                        observer.OnNext(loc.Value);
                }
            }
    
            public void EndTransmission()
            {
                foreach (var observer in observers.ToArray())
                    if (observers.Contains(observer))
                        observer.OnCompleted();
    
                observers.Clear();
            }
        }
    
        public class MessageUnknownException : Exception
        {
            internal MessageUnknownException()
            {
            }
        }
    
        public class Inspector : IObserver<Message>
        {
            private IDisposable unsubscriber;
            private string instName;
    
            public Inspector(string name)
            {
                this.instName = name;
            }
    
            public string Name
            {
                get
                {
                    return this.instName;
                }
            }
    
            public virtual void Subscribe(IObservable<Message> provider)
            {
                if (provider != null)
                    unsubscriber = provider.Subscribe(this);
            }
    
            public virtual void OnCompleted()
            {
                Console.WriteLine("The headquarters has completed transmitting data to {0}.", this.Name);
                this.Unsubscribe();
            }
    
            public virtual void OnError(Exception e)
            {
                Console.WriteLine("{0}: Cannot get message from headquarters.", this.Name);
            }
    
            public virtual void OnNext(Message value)
            {
                Console.WriteLine("{1}: Message I got from headquarters: {0}", value.Text, this.Name);
            }
    
            public virtual void Unsubscribe()
            {
                unsubscriber.Dispose();
            }
        }
    
        public class Program
        {
            public static void Main(string[] args)
            {
                Inspector inspector1 = new Inspector("Greg Lestrade");
                Inspector inspector2 = new Inspector("Sherlock Holmes");
    
                Headquarters headquarters = new Headquarters();
    
                inspector1.Subscribe(headquarters);
                inspector2.Subscribe(headquarters);
    
                headquarters.SendMessage(new Message("Catch Moriarty!"));
                headquarters.EndTransmission();
    
                Console.ReadKey();
            }
        }