Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C#

Observer Pattern in C#

Rate me:
Please Sign up or sign in to vote.
5.00/5 (12 votes)
11 Sep 2022MIT3 min read 18K   252   45   4
Tutorial article on Observer pattern in C#
This is a tutorial article on the Observer pattern in C#. We first present the Classic Observer pattern which is one of the GoF patterns. Observer pattern is integrated into C# via the usage of Event mechanism and that is discussed next. The intended audience is Intermediate C# programmers and above.

Introduction

This is a tutorial article on Observer pattern in C#. The intended audience are Intermediate C# programmers and above. We first present Classic Observer pattern which is one of GoF patterns and is often mentioned in literature.

Modern languages like C# have integrated Event mechanism, via which Observer pattern has practically been integrated into language mechanisms. Modern usage of Observer pattern in C# is practically related to usage of Event mechanism and Classic Observe pattern is made obsolete in C#.

The fact that Event mechanism in reality provides Synchronous calls is often overlooked and not emphasized enough. Programmers often have illusion of parallelism, which is not reality, and is an important issue in today’s multi-core-processors world.

The code presented is tutorial, demo-of-concept level and for brevity, does not handle or show all variants/problematic issues.

Classic Observer Pattern

Observer pattern, or sometimes called Subject-Observer pattern, defines a one-to-many dependency between objects, so that when one object (Subject) changes its state, all its dependent objects (Observers) are notified and updated. We say that Observer “subscribes” with Subject to get notified of Subject’s state change. In simple words, one component (Subject) notifies other components (Observers) that state changed.

While in C#, Event keyword is already integrated, that is implementation of Observer pattern, one can still decide to roll-out one's own version/implementation. Here is how Classic Observer, as described in GoF literature looks like.

Class diagram:

Image 1

Implementation code sample:

C#
public class Args
{
}

public abstract class ASubject
{
    protected List<IObserver> observersList = new List<IObserver>();

    public void AddObserver(IObserver observer)
    {
        observersList.Add(observer);
    }

    public void RemoveObserver(IObserver observer)
    {
        observersList.Remove(observer);
    }
}

public interface IObserver
{
    void Update(object subject, Args args);
}

public class Subject : ASubject
{
    public string SubjectState = null;

    public void NotifyObservers()
    {
        ArgsSubject args = new ArgsSubject();
        args.SubjectState = this.SubjectState;

        foreach (IObserver o in observersList)
        {
            o.Update(this, args);
        }
    }
}

public class Observer : IObserver
{
    private string name;
    private string observerState;

    public Observer(string name)
    {
        this.name = name;
    }

    public void Update(object s, Args args)
    {
        observerState = ((ArgsSubject)args).SubjectState;
        Console.WriteLine("Observer {0}'s new state is {1}",
            name, observerState);
    }
}

public class ArgsSubject : Args
{
    public string SubjectState = null;
}

class Client
{
    public static void Main(string[] args)
    {
        Subject s = new Subject();
        s.AddObserver(new Observer("1"));
        s.AddObserver(new Observer("2"));
        s.AddObserver(new Observer("3"));

        // Change subject state and notify observers
        s.SubjectState = "ABC123";
        s.NotifyObservers();

        Console.ReadLine();
    }
}

Sample execution:

Image 2

Modern Observer Pattern in C# -Events

C# has already integrated Event keyword, which is the implementation of Observer pattern. C# is bringing its own terminology:

  • Subject -> Event
  • Observer -> EventHandler (more precisely, Observer.Notify() method is mapped to EventHandler method, Observer object itself is considered obsolete)
  • SubjectStateInfo -> EventArgs (more precisely SubjectStateInfo is mapped to attribute of class inherited from EventArgs)
  • AddObserver() method -> operator +=
  • RemoveObserver() method -> operator -=

C# literature advices of using of EventHandler delegates of certain signature, with parameters (this, EventArgs), but nothing in C# language prevents one from using the signature he likes.

Here is the class diagram of the new solution:

Image 3

And here is the code:

C#
public class Subject
{
    public event EventHandler<EventArgsSubject> SubjectEvent;

    public string SubjectState;

    public void NotifyObservers()
    {
        EventArgsSubject args = new EventArgsSubject();
        args.SubjectState = this.SubjectState;

        // (1) 
        if (SubjectEvent != null)
        {
            SubjectEvent(this, args);
        }
    }
}

public class Observer
{
    private string name;
    private string observerState;

    public Observer(string name)
    {
        this.name = name;
    }

    public void Update(object subject, EventArgsSubject args)
    {
        observerState = args.SubjectState;
        Console.WriteLine("Observer {0}'s new state is {1}",
            name, observerState);
    }
}

public class EventArgsSubject : EventArgs
{
    public string SubjectState = null;
}

class Client
{
    public static void Main(string[] args)
    {
        Subject s = new Subject();
        s.SubjectEvent += (new Observer("1")).Update;
        s.SubjectEvent += (new Observer("2")).Update;
        s.SubjectEvent += (new Observer("3")).Update;

        // Change subject state and notify observers
        s.SubjectState = "ABC123";
        s.NotifyObservers();

        Console.ReadLine();
    }
}

And here is the sample execution:

Image 4

Let us mention that in (1), we used a method that is popular in literature. But, theoretically speaking, a “thread race” condition is possible, between time SubjectEvent is checked for null, and events are invoked, some other thread can remove handlers from the SubjectEvent, which would result in null reference exception. A more popular way to write invocation of handlers today is:

C#
SubjectEvent?.Invoke(this, args);

Event Mechanism Provides Synchronously Calls, on a Single Thread

What needs to be emphasized, is that in calls:

C#
if (SubjectEvent != null)
{
    SubjectEvent(this, args);
}

//or

SubjectEvent?.Invoke(this, args);

subscribed EventHandlers are being invoked synchronously on a single thread. That has some not so obvious consequences:

  • EventHandlers are executed in sequence, one after another, in the order in which they are subscribed to the event.
  • That means that objects/values in earlier subscribed EventHandler are updated earlier then in other EventHandlers, which might have consequences to program logic.
  • Call to certain EventHandler blocks the thread until all work in that EventHandler is completed.
  • If Exception is thrown in certain EventHandler, all EventHandlers subscribed after that one will not be executed.

Conclusion

The observer pattern is a very important pattern and has been directly supported by C# language by the usage of Events. Though threading issues can be tricky, the programmer must have a good understanding of how the Notification mechanism works from a threading prospective.

History

  • 7th March, 2022: Initial version
  • 11th September, 2022: Decided to break the article into 2 parts

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer
Serbia Serbia
Mark Pelf is the pen name of just another Software Engineer from Belgrade, Serbia.
My Blog https://markpelf.com/

Comments and Discussions

 
GeneralMy vote of 5 Pin
Gian Nebiacolombo11-Sep-22 23:13
professionalGian Nebiacolombo11-Sep-22 23:13 
QuestionTry reactivate extensions Pin
Chad Rotella9-Mar-22 13:44
professionalChad Rotella9-Mar-22 13:44 
AnswerRe: Try reactivate extensions Pin
Dan Spear12-Sep-22 3:26
Dan Spear12-Sep-22 3:26 
GeneralRe: Try reactivate extensions Pin
Randall Woodman12-Sep-22 8:31
Randall Woodman12-Sep-22 8:31 

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.