Observer Pattern in C# using Delegates and Events

In C++, the Observer design pattern (also known as Publisher/Subscriber) is usually implemented as suggested in the bible of design patterns.

This implementation requires that Observer and Observed implement the IObserver and IObserved interfaces, so that when Observed changes, all its registered Observers are notified.

Needless to say, this can be done the same way in C#. However, this is not necessary since the Observer pattern is implemented in the C# language itself, with its delegates and events constructs.

Suppose we want to use this to implement the MVC architecture pattern in our C# application. The MVC rationale says that View objects can reference Model objects, but not the other way around. So how does the model communicate with the view? Using the Observer Pattern.

First, in our Model class, we must declare the Event which will be triggered when the Model changes. Then, we must define the delegate associated to this event's handlers.

namespace App.Model{

    public class Observed{
        // State
        // ...
        // Observer pattern state
        // Delegate observers must implement
        public delegate void ModelChangedHandler(object model, EventArgs e);
        // Event to which observers must subscribe
        public event ModelChangedHandler eModelChanged;
        // Behavior
        // ...
        //Method to invoke whenever state changes
        protected void OnModelChange(object model, EventArgs e){
            //If there are observers, notify them
            if (eModelChanged != null){
                eModelChanged(this, e);
            }
        }
        //Some method which changes state and wants to notify observers
        public void f(){
           //After doing stuff which changes state...
           OnModelChange(this, e); // Fill e with proper info or pass null
        }
    }

    //If we want to pass info, we can do something like this
    public class ModelChangedEventArgs : EventArgs{
        //State with info to pass
        //Behavior to update/compute that info
    }
}

Finally, in the View class, we must implement a method with the same header as the delegate, and subscribe to the Model's event registering said method as the handler:

namespace App.View{
    public partial class View : Window{
        // State
        // ...
        // Reference to Model
        public Model.Observed Model { get; set; }
        // Behavior
        public View(){
            this.InitializeComponent();
            // Create modelo and subscribe to its event
            _model = new Model.Observed();
            _model.eModelChange += new Model.Observed.ModelChangedHandler(ModelChanged);
            // Init more stuff...
        }
        // Event handler
        public void ModelChanged(object model, EventArgs e){
            if (this.Dispatcher.CheckAccess()){
                //Act according to changes
            } else {
                // This thread cannot access the GUI; put it in dispatcher
                this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                        (Action)(() =>
                        {
                            //Act according to changes
                        }));
            }
        }
}

About the last part, in .NET, only the Window Thread can access the GUI. The BeginInvoke approach used allows us to temporarily bypass that restriction.

IMPORTANT: For each +=, there must be a -= once we're done using the event, when we no longer want to handle the event, or when the subscriber is disposed. This is a very common source of memory and resource leaks in .NET, so be very careful.

Comments

  1. It is really a great work and the way in which u r sharing the knowledge is excellent.
    Thanks for helping me to understand basic concepts. As a beginner in Dot Net programming your post help me a lot.Thanks for your informative article.. http://www.credosystemz.com/training-in-chennai/best-dotnet-training-in-chennai

    ReplyDelete
  2. Thanks guys! I'm glad this helped someone other than me

    ReplyDelete

Post a Comment

Popular posts from this blog

VB.NET: Raise base class events from a derived class

Apache Kafka - I - High level architecture and concepts

Upgrading Lodash from 3.x to 4.x