Avoid This Trap As We Implement The Observer Pattern In C#
November 17, 2023
• 1,012 views
Have you heard of the observer pattern? It's one of the many design patterns we get exposed to when we're starting off in our programming journeys. However, if you're like me, you might not have been shown this explicitly but instead stumbled upon things that seem similar enough. So with this video, I'll show you how to implement the observer pattern in CSharp with events and the pros and cons of doing so!
Have you subscribed to my weekly newsletter yet? A 5-minute read every weekend, right to...
View Transcript
there are many design patterns that help us do many different things and one really popular design pattern that we get to learn very early on in programming is the Observer pattern The Observer pattern as the name suggests allows us to create systems where different components are able to observe different changes on the thing that they're observing and that means that if you have a system that has different components that are interested in changing states or events things like that then the Observer pattern is really helpful in assisting with that in this video I'm going to walk us through a simple implementation of the Observer pattern in C using events after we go through the benefits and the drawbacks of this Approach at the end of this video I'll show you a different way that you can mitigate these before I head over to visual studio
just a quick reminder to subscribe to My Weekly Newsletter it's totally free there's exclusive articles and early access to videos just like this one I'll put a link in the comments below so let's head over to visual studio and check out the Observer pattern here in Visual Studio I've just created a very simple implementation of the Observer pattern and this is going to be using build buil-in events that we have available to us in C what we have when we're looking at the Observer pattern is this concept of something that we can observe so that is called an observable so that's the implementation I have right here that we'll look at we have things that can observe the observable which is going to be this class that I have here which we'll also look at and then of course you have something that you want
to be passing between them so some type of message or it could be as simple as a string in this case because I'm using events I am using an event args class and if we start with this one to have a peak we can see that this event args class is truly doing something as simple as just having a message so the only reason this is a little bit more of a Bose and more than just a string is because of this event handler syntax which we'll look at next when it comes to events and event handlers if I jump over to this observable class that we have we'll see how the basics of this work if you're not totally familiar with event handlers and the event syntax that's available in C just a quick little recap on how that works is that this public
event event handler with the type of the event args that we're interested in gets a name that we'll just call event for this example but two important pieces that we want to look at are this keyword event and what that will allow us to do is have subscribers to this event write syntax that looks just like event and then they can use plus equals and provide an event handler and the other thing that they can do is minus equals to remove their event handler from that so that's what the event keyword right here allows callers of this class to do and the next part is this event handler is providing a signature that gets used and then the common practice with event handlers is that you have something like an object which is the sender so in this case it would be this observable is
the sender and we can see that right here on line 22 where we're passing in this when we invoke the event the next part is really just the type of the event arcs and you don't need this it's only only if you need custom event args so technically you could have an event handler that looks just like this and have event args that get passed in that don't really have any data that get passed in so for us we're going to be using the special message event args and that's going to allow us to pass in this string that is the message so if you think about how this entire class is laid out the idea is really that it has an event that people can subscribe to or in the language that we're using with the Observer Paradigm it's really that they can observe
an event and that we can fire the event from this Observer and all that it's going to do is allow someone on the outside to call this method so they'll call fire event pass in a message here and then anyone that is subscribed to this event because they were able to access it right here using that plus equal syntax that I showed you this line here on 22 is going to trigger each one of those callers that's subscribed and that will mean in the order that the they're subscribed this event handler invoke method will go call each one of those in order now that we've seen how the observable class Works let's jump down to the Observer now there's many different ways to do this and I'm going to show you one way that people implement this and then we're going to talk through some
drawbacks of doing this if you stay right to the end of this video I'm going to show you that there's other ways that we can manage this so that we don't have to worry about some of these challenges we can see that in the Constructor what we're doing is getting an instance of that observable so the class that we just looked at we taken an instance of that and then as I was describing we're able to access that event and then subscribe our own event handler to it if you recall if we start looking at line 33 I mentioned that the syntax that signature that we're going to be using for that event handler has an object that's a center and what we could expect is when this is invoked we would be getting a reference to that observable and just to point it out
line 22 the first parameter is an instance of the observable so that's going to be passed in here when this method is called and then we get that message event args which is going to be right here on line 22 that's going to be the instance that finally gets its way all the way to here and that means we have access to that message string and we can see that on line 35 so all we're doing in this onevent Handler is right into the console that we got the event and then we're going to print out the message that we received so now we've seen the Observer and the observable so let's go up to the top and kind of look through this little example that we have here so all that I've done is I've declared an instance of the observable and then I've
made one Observer and just by creating the Observer and passing in the observable we're hooking up to that event so already by the time we reach line four our Observer is listening to the observable and observing any events that might be raised let's go ahead and run this and see what's printed to the console and as we can see in the output we are getting that event handler running where the console right line code was we can see that it says event fired and then that message hello world which is passed in right at the top level makes its way all the way through the observable where we're calling fire event with that parameter and then it makes its way through those different event handlers and in this case it's only one because we only have one Observer so I mentioned that there's different ways
that you can implement this pattern and we're looking at one because the event and event handler syntax is built into C and it lends itself really well to designing systems that make it obvious for who's owning the event and who's able to listen to it that plus equals and minus equals syntax to register and unregister event handlers is super convenient but there are drawbacks that we should talk about with using this and they're not necessarily limited to The Observer pattern so it's more about implementing it this way with events and event handlers in the code I demonstrated that the Constructor of our Observer is able to wire itself up to the observable and that means just by creating a new instance of the Observer we're already registering to listen for events to a caller this might seem super convenient because all you have to do
is keep creating new instances of this and you keep getting new hookups to that event and that means it only takes a new instance to start observing more from the observable so what's the drawback with this well if you understand more about how events and event handlers work in C one of the challenges is that when you're using that plus equal syntax to hook up your event handler you end up getting a reference to the object that is holding the definition of that method signature so that doesn't sound so bad and truly if you're using these objects for the lifetime of an application it's not really an issue the problem is when these objects have a different lifetime scope than the entire length of your application so when that happens you end up creating these objects and they wire up their event but they never
actually remove themselves from that event handler so you might think that your object that is observing goes out of scope it's no longer necessary but there's no way to unhook right now some ways that people get around this involve taking their Observer and making it eye disposable and that way they can have that using syntax and when they're disposing their Observer they can unhook from the observable this does mean that with your Observer this instance that you're creating it has full control over when it's able to hook up and be removing itself from the observable and that might be a nice way to handle things another way that you could look at this is taking the ownership of wiring up the Observer and the observable outside of the Observer so the Observer itself is only responsible for being able to observe it doesn't have any
concept of how it gets wired up to the observable by moving that logic out you can place the ownership of how to connect those two classes together into a third class whose single responsibility right single responsibility principle its sole purpose is to do the wiring up of the Observer to the observable and tearing it down as necessary so having more explicit ownership might allow you to structure your code in a way that is a lot more deterministic and you can write tests and make sure that things are hooked up and torn down as you need them one more drawback that we have when using events with event handlers for the Observer pattern is asynchronous code and odds are if you've been writing C for at least a little bit you've probably used the task object and had async await methods and doing that and combining
it with events and event handlers is just not a recipe that you want to sign up for if you want to understand some of the challenges with doing that you can watch a video that'll link right up here which thoroughly explains the complications what we've seen so far is that we're able to wire up an observer to an observable using events and some of the drawbacks to that implementation however if we move to a whole new type of implementation of this Observer pattern we can absolutely work around some of the challenges that we talked about so let's ditch events and you can watch the next video when it's ready right here and it'll explain everything you need to do thanks and we'll see you next time
Frequently Asked Questions
What is the Observer pattern and how is it implemented in C#?
The Observer pattern is a design pattern that allows different components to observe changes in another component. In C#, I implement it using built-in events. An observable class raises events that observers can subscribe to, allowing them to react to changes.
What are some drawbacks of using the Observer pattern with events in C#?
One major drawback is that when observers are created, they automatically subscribe to events, which can lead to memory leaks if they are not properly unhooked. Additionally, managing the lifecycle of observers can be challenging, especially in asynchronous scenarios.
How can I mitigate the challenges of using the Observer pattern in C#?
To mitigate these challenges, I recommend making observers IDisposable to allow them to unhook themselves from events when they are no longer needed. Alternatively, you can separate the wiring logic into a third class to manage the connections more explicitly.
These FAQs were generated by AI from the video transcript.