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:
Implementation code sample:
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"));
s.SubjectState = "ABC123";
s.NotifyObservers();
Console.ReadLine();
}
}
Sample execution:
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:
And here is the code:
public class Subject
{
public event EventHandler<EventArgsSubject> SubjectEvent;
public string SubjectState;
public void NotifyObservers()
{
EventArgsSubject args = new EventArgsSubject();
args.SubjectState = this.SubjectState;
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;
s.SubjectState = "ABC123";
s.NotifyObservers();
Console.ReadLine();
}
}
And here is the sample execution:
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:
SubjectEvent?.Invoke(this, args);
Event Mechanism Provides Synchronously Calls, on a Single Thread
What needs to be emphasized, is that in calls:
if (SubjectEvent != null)
{
SubjectEvent(this, args);
}
SubjectEvent?.Invoke(this, args);
subscribed EventHandler
s are being invoked synchronously on a single thread. That has some not so obvious consequences:
EventHandler
s 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 EventHandler
s, 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 EventHandler
s 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