Click here to Skip to main content
14,128,868 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

39K views
18 bookmarked
Posted 17 Jun 2010
Licenced CPOL

Observer in .NET 4.0 with IObserver(T)

, 17 Jun 2010
Rate this:
Please Sign up or sign in to vote.
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:

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.

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.

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.

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.

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Mattia Baldinger
Software Developer
Switzerland Switzerland
No Biography provided

You may also be interested in...

Comments and Discussions

 
GeneralThanks Pin
jcddcjjcd25-Jun-10 21:43
memberjcddcjjcd25-Jun-10 21:43 
GeneralMy vote of 2 Pin
Richard Ashman22-Jun-10 1:31
memberRichard Ashman22-Jun-10 1:31 
GeneralRe: My vote of 2 Pin
fatho122-Jun-10 1:37
memberfatho122-Jun-10 1:37 
GeneralRe: My vote of 2 PinPopular
Richard Ashman22-Jun-10 2:35
memberRichard Ashman22-Jun-10 2:35 
GeneralMy vote of 2 Pin
Emile van Gerwen21-Jun-10 22:23
memberEmile van Gerwen21-Jun-10 22:23 
GeneralRe: My vote of 2 Pin
AndreBroz22-Jun-10 0:27
memberAndreBroz22-Jun-10 0:27 
GeneralRe: My vote of 2 Pin
Mattia Baldinger22-Jun-10 1:37
memberMattia Baldinger22-Jun-10 1:37 
GeneralRe: My vote of 2 Pin
Mattia Baldinger22-Jun-10 1:27
memberMattia Baldinger22-Jun-10 1:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web04 | 2.8.190518.1 | Last Updated 17 Jun 2010
Article Copyright 2010 by Mattia Baldinger
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid