Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Observer in .NET 4.0 with IObserver(T)

0.00/5 (No votes)
17 Jun 2010 1  
The IObservable(T) and IObserver(T) interfaces are part of base class library of .NET 4.0. It's great that there is now a out of the box solution to implement the observer pattern. This was overdue, because other languages provide classes to implement this pattern already since a long time.

The IObservable(T) and IObserver(T) interfaces are part of the base class library of .NET 4.0. It's great that there is now an out of the box solution to implement the observer pattern. This was overdue, because other languages provide classes to implement this pattern already since a long time. It is one of the most used and normally part of every good software design.

The observer pattern contains a class (normally the observable itself) that maintains a list of its dependants (the observers) and notifies them automatically of changes. With interfaces (one for the observer and one for the observable), like IObservable(T) and IObserver(T), it is possible to implement the pattern, so the observable doesn't really know from which classes it will be observed. The interfaces are in the system namespace and look as follows:

C#
namespace System
{
    public interface IObservable
    {
        IDisposable Subscribe(IObserver observer);
    }

    public interface IObserver
    {
        void OnCompleted();
        void OnError(Exception error);
        void OnNext(T value);
    }
}

The observable must implement the subscribe method where observers can be attached. It returns an object of the type IDisposable which can be used to unsubscribe by calling Dispose. The observer has to implement three methods, OnCompleted which is called to indicate that the observable has finished sending notifications, OnError when an error occurred, and OnNext when new data is available (the method is also called update in other languages).

I will show in a simple example how it works. But first we need a base class for all observables which separates the specific code from the standard observable code. This is something which isn't part of the base class library. Normally an observable can be implemented without any additional code (only the method call which indicates a change). The following code shows a simple generic approach for such an observable base class.

C#
public class Observable<T>: IObservable<T>
{
    private List<IObserver<T>> observers;

    public Observable()
    {
        observers = new List<IObserver<T>>();
    }

    protected void Notify(T obj)
    {
        foreach (IObserver<T> observer in observers)
        {
            observer.OnNext(obj);
        }
    }

    public IDisposable Subscribe(IObserver<T> observer)
    {
        if (!observers.Contains(observer))
        {
            observers.Add(observer);
        }

        return new Unsubscriber(observers, observer);   
    }

    private class Unsubscriber : IDisposable
    {
        private List<IObserver<T>> observers;
        private IObserver<T> observer;

        public Unsubscriber(List<IObserver<T>> observers, IObserver<T> observer)
        {
            this.observers = observers;
            this.observer = observer;
        }

        public void Dispose()
        {
            if (observer != null && observers.Contains(observer))
            {
                observers.Remove(observer);
            }
        }
    }
}

It already implements the IObservable(T) and so contains the subscribe method. It returns an instance of the Unsubscriber class, which is a simple inner class that implements the IDisposable interface. Also there is a method, which can be called to notify all observers that there are changes in the observed object. As an example, we use a class which represents a location, which inherits from the Observable class.

C#
public class Location : Observable<Location>
{
    private double longitude = 0 ;
    private double latitude = 0;

    public double Longitude
    {
        get { return longitude; }
        set
        {
            longitude = value;
            Notify(this); 
        }
    }

    public double Latitude
    {
        get { return latitude; }
        set
        {
            latitude = value;
            Notify(this); 
        }
    }
}

With the base class, the concrete observable has only to call Notify. The whole code which is in the base class can also be in the observable (in this case the Location class) itself, for example when you already inherit from another class. The observer tracks the location and displays it when it changes.

C#
public class Tracker : IObserver<Location>
{
    public void OnCompleted()
    {
        Console.WriteLine("untracked"); 
    }

    public void OnError(Exception error)
    {
        //some error handling
    }

    public void OnNext(Location value)
    {
        Console.WriteLine("Longitude: "+ value.Latitude.ToString()+" "+
                            "Latitude: "+ value.Longitude.ToString());
    }
}

The OnNext method writes the location into the console. The following code can be used to execute the example.

C#
class Program
{
    static void Main(string[] args)
    {
        var location = new Location();
        var tracker = new Tracker();
        var unsubscriber = location.Subscribe(tracker);
           
        double latitude = 37.44;
        double longitude = -122.14; 

        for(int i = 0 ; i < 100 ; i++)
        {
            Thread.Sleep(100);
            latitude -= 0.1;
            longitude += 0.1;

            location.Latitude = latitude; 
            location.Longitude = longitude;             
        }

        unsubscriber.Dispose(); 
    }
}

So these two interfaces, IObservable(T) and IObserver(T), give you the ability to implement the pattern. For me, the naming of the methods is a little bit different than expected and also the concept of unsubscription is confusing at the beginning. Also a little bit disappointing is that a base class for the observable is missing. But after all they did a good job providing a standardized way.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here