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.
It is really a great work and the way in which u r sharing the knowledge is excellent.
ReplyDeleteThanks 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
Thanks guys! I'm glad this helped someone other than me
ReplyDelete