class Observer : IObserver
{
public Observer(string name){ m_name = name; }
private string m_name;
public string Name
{
get{ return m_name; }
set{ m_name = value; }
}
#region IObserver Members
public void Pinged(ISubject subject, string message)
{
Subject s = subject as Subject;
Console.WriteLine(s.Name + " notified " + m_name + " with message: " + message);
}
#endregion
}
class MainProgram
{
[STAThread]
static void Main(string[] args)
{
Subject Movie = new Subject("Blade Runner");
Observer Frank = new Observer("Frank");
Movie.AddObserver(Frank);
Observer Daniel = new Observer("Daniel");
Movie.AddObserver(Daniel);
Movie.NotifyObservers("started");
Movie.NotifyObservers("intermission");
Movie.NotifyObservers("ended");
}
}
C# Events
Like we talked about earlier, C# events are nothing more than a implementation of the Observer pattern we worked with earlier in this article with some minor changes. The biggest change is that our ISubject and IObserver interfaces are no longer required because we will be doing all of our notification through a delegate. (If you are not familiar with delegates, I have a brief article on them here).
We will be following the basic structure of our implementation of the Observer pattern we talked about earlier to make it easier to compare the two. First of all we need our delegate that gives us some flexibility on the name and location of the method we will be using to "Ping" the observers.
delegate void NotifyHandler(Subject subject, string message);
The Subject
The subject has changed a bit. The event takes place of the array list in our first example. Instead of calling an AddObserver() method, we will be adding a delegate to the event with the += operator. Instead of calling a NotifyObsevers() method, we will be calling FireEvent() and iterating through the delegates in the event and firing each one (we are not required to do this, but this is the best way to see how the C# event model parallels the Observer pattern).
class Subject
{
public Subject(string name){ m_name = name; }
private string m_name;
private event NotifyHandler m_notifyHandler;
#region Events
public event NotifyHandler NotifyObservers
{
add
{
Console.WriteLine(((Observer) value.Target).Name + " is now observing " + m_name);
m_notifyHandler += value;
}
remove { m_notifyHandler -= value; }
}
#endregion
#region Event Handling
public void FireEvent(string message)
{
if(null != m_notifyHandler)
{
foreach(System.Delegate del in m_notifyHandler.GetInvocationList())
{
del.DynamicInvoke(new object[] {this, message});
}
}
}
#endregion
}
The Observer
The observer is about the same. Instead of implementing an interface, our observer just has a method with the same signature as the delegate that the subject will be firing off. Usually this method name starts with "On" (like with OnClick, OnLeave, etc).
class Observer
{
public Observer(string name) { m_name = name; }
private string m_name;
public string Name
{
get{ return m_name; }
set{ m_name = value; }
}
public void OnPinged(Subject subject, string message)
{
Console.WriteLine(subject.Name + " notified " + m_name + " with message: " + message);
}
}
Registering
The wire-up is similar to the observer pattern except there is a special syntax for registering with the event and we are now registering delegates to the OnPinged() method in Frank and Daniel instead of having to implement an interface method.
[STAThread]
static void Main(string[] args)
{
Subject Movie = new Subject("Blade Runner");
Observer Frank = new Observer("Frank");
Movie.NotifyObservers += new NotifyHandler(Frank.OnPinged);
Observer Daniel = new Observer("Daniel");
Movie.NotifyObservers += new NotifyHandler(Daniel.OnPinged);
Movie.FireEvent("started");
Movie.FireEvent("intermission");
Movie.FireEvent("ended");
}
Wrap up
Hopefully this article has helped you gain a better understanding of events in C#. As you can see, the event model is a direct implementation of the Observer pattern. I just wanted to point out a couple more things.
Usually you'll see events fired off with the following syntax which iterates through each of the delegates in the event and fires each one in turn:
public void FireEvent2(string message)
{
if(null != m_notifyHandler)
{
m_notifyHandler(this, message);
}
}
To unregister for an event you can use the -+ syntax.
Movie.NotifyObservers -= new NotifyHandler(Frank.OnPinged);
Until next time,
-Happy coding