C# event handlers : when are the subscribers invoked? - c#

After doing some reading here is what I think is happening, and I am just looking for confirmation or correction.
The samples below will be using anonymous lambdas for brevity, obviously doing that you would lose the ability to unsubscribe.
MyEvent += (sender, args) => {Console.WriteLine("One")};
MyEvent += (sender, args) => {Console.WriteLine("Two")};
MyEvent += (sender, args) => {Console.WriteLine("Three")};
With the subscribers in place, we would invoke the Event:
var handler = MyEvent;
if(handler != null){
handler(this, EventArgs.Empty) // <-- Interested in this moment
}
So here is where I will need correcting/clarification/guidance. As I understand it, what invoking handler is effectively doing is (I understand it's not doing EXACTLY this, this is for illustrative purposes only).
foreach(var subscriber in self){
subscriber(sender, args);
}
I'm explicitly not talking about using this with BeginInvoke. So what happens is basically invoking the handler causes the handler to loop through all of it's subscribers in some undefined order and invoke them passing the appropriate args from the invoking thread (in this example, again not talking about BeginInvoke)
Put another way, handler(this, EventArgs.Empty) is basically just doing this immediately, on the invoking thread:
anonymous1(..., ...)
anonymous2(..., ...)
anonymous3(..., ...)
Modifying the above slightly:
var handler = MyEvent;
if(handler != null){
handler(this, EventArgs.Empty) // <-- Interested in this moment
}
Console.WriteLine("Done Invoking Subscribers");
We can always expect the output to be (since none of our subscribers are async and we are not using BeginInvoke):
// some undefined order of the following 3 lines:
One
Two
Three
// Always followed by
Done Invoking Subscribers
Generally speaking, do I have this idea right?

Yes
Sorry to be that direct but the simple answer is yes, that's how it works.

Related

Why copy an event handler into another variable

In an example, I find this code:
public event EventHandler ThresholdReached;
protected virtual void OnThresholdReached(EventArgs e)
{
EventHandler handler = ThresholdReached;
if (handler != null)
handler(this, e);
}
I would like to understand the reason for the line:
EventHandler handler = ThresholdReached;
Can we not just do it this way:
public event EventHandler ThresholdReached;
protected virtual void OnThresholdReached(EventArgs e)
{
if (ThresholdReached != null)
ThresholdReached(this, e);
}
Are there some advantages/disadvantages to either way of doing it?
The problem is that between this line
if (ThresholdReached != null)
and this line
ThresholdReached(this, e);
a different thread could have removed the handlers from the ThresholdReached event. So it would be null and an exception would be thrown.
By storing the value in a local variable, you make the call thread-safe.
Since C# 6 you can shorten the code to this
ThresholdReached?.Invoke(this, e);
Now the compiler takes care of storing the value in a temporary variable.
There's a theoretical risk of a thread race in the second version where someone unsubscribes the event between the check and the invoke, causing a NullReferenceException in the invoke step. Capturing the value into a local and testing/invoking that prevents this. However, perhaps use the third version, possible with C# 6 or above (thanks #Cid):
ThresholdReached?.Invoke(this, e);
This is basically a short-hand version of the first version - all the safety, but now with terseness.

C# events initialisation

Consider the "application" where an object (Thrower) is throwing a ball to another object (Receiver). The event (BallIsThrowed) happens when the ball is thrown.
Here are the 2 classes :
then the entry point :
And finally the methods pointed by the delegate when events are fired :
This is working well.
Now I want to comment this line :
because I want to say that the ball was not thrown.
The result is a null Exception :
This is normal because at this point BallIsThrowed is null
To solve this, I initilise my event :
But then my problem is that my code is never taking the event when I decomment "receiver.Register(thrower)"
My questions are :
How can I have the 2 method EventMethod fired ?
The best practice way to fire an event looks like this:
EventHandler ballIsThrowed = BallIsThrowed;
if (ballIsThrowed != null)
{
ballIsThrowed(this, EventArgs.Empty);
}
The reason for the temporary variable is to prevent race conditions between the null check and the execution.
Bear with me: you are setting an event handler to BallIsThrowed, then you use IthrowTheBall() to actually trigger the event, with this code:
BallIsThrowed(this, EventArgs.Empty);
When you try to call an event handler, in this case the BallIsThrowed, it must exists or it will throw a NullReferenceException. So, in order for this to work, it must exists an event handler BallIsThrowed. It actually does, until you comment this line:
//BallIsThrowed += new EventHandler(method.EventMethod2);
So basically you need to verify if the EventHandler exists before firing it:
if (BallIsThrowed != null)
BallIsThrowed(this, EventArgs.Empty);
Another (more elegant, if you ask me) way of doing this is:
(BallIsThrowed??delegate{})(this, EventArgs.Empty);
Compact, thread safe...
Two things that need to be corrected:
In your Event Invoker, check EventHandler != null because you dont know if anyone registered to your handler.
var ballThrown = BallIsThrowed;
if (ballThrown != null)
{
ballThrown(this, EventArgs.Empty);
}
For general knowledge, In order to register more then one delegate to your
EventHandler, dont register it via the = operator, but via the
+= operator, meaning you want to append a new delegate:
public void IThrowTheBall()
{
// Do stuff
// You dont have to register this delegate, you can append it to the current
// delegates already registered
BallIsThrowed += method.EventMethod1;
}

Handling events with C# Extension Methods

I recently learned about using C# extension methods to make calling events easier and I've been using them more and more. I recently hit a strange issue that I don't understand though, and I was wondering if someone could explain it.
The issue occurs when trying to set an eventhandler extension method as an event handler of another event. Here is an example of what I'm doing:
public static class EventHandlerExtensions
{
public static void Raise<TEventArgs>(
this EventHandler<TEventArgs> eventHandler,
object sender, TEventArgs args) where TEventArgs:EventArgs
{
if (eventHandler != null)
{
eventHandler(sender, args);
}
}
}
public class Test
{
private event EventHandler<EventArgs> EventA;
private event EventHandler<EventArgs> EventB;
public Test()
{
Console.WriteLine("::Start");
EventB += EventA.Raise;
EventA += (s, a) => Console.WriteLine("Event A raised");
EventB.Raise(this, EventArgs.Empty);
Console.WriteLine("::End");
}
}
In this example, EventA should be triggered as a result of EventB being triggered. However, when I run this code, EventB fires, but the extension method on A doesn't find any listeners for it.
If I change the order around, everything works fine:
Console.WriteLine("::Start");
EventA += (s, a) => Console.WriteLine("Event A raised");
EventB += EventA.Raise;
EventB.Raise(this, EventArgs.Empty);
Console.WriteLine("::End");
Also, calling EventA.Raise from a lambda works fine:
Console.WriteLine("::Start");
EventB += (s, a) => EventA.Raise(s, a);
EventA += (s, a) => Console.WriteLine("Event A raised");
EventB.Raise(this, EventArgs.Empty);
Console.WriteLine("::End");
This is just a simple example, but I'm trying to create a class which can re-dispatch events of event sources added to it in the cleanest way possible. I don't want to create named methods just for redispatching the same events, and I'd rather not store lists of lambda functions that I can unhook from the event handlers later. Mostly, I'm just curious as to why this is happening?
Any ideas?
You capture old value of EventA into the closure by your Raise function. Since later you use += it changes value of EventA, but your closure still have an old value.
You code:
EventB += EventA.Raise;
EventA += (s, a) => Console.WriteLine("Event A raised");
Can be expanded into equivalent code which makes it clear why you get old delegate:
var oldEventA = EventA;
EventB += oldEventA.Raise; // captures old value here
// now EventA changed to new value
EventA = oldEventA + ((s, a) => Console.WriteLine("Event A raised");)
You can add following to before EventB += EventA.Raise to verify that code actually raises old event for A:
EventA += (s, a) => Console.WriteLine("Old Event A raised");
Delegate objects are immutable. Much like strings. So when you assign EventA, you create a new object. EventB is still targeting the old one, the one that didn't have any event handler assigned yet. You have to swap the two statements to fix the problem.

Detaching an handler the first time you call it

I would like to call the event handler just one time, and then detach it. I tried to write:
EventHandler handler = (s, e) =>
{
// Do something
//
// blabla
// Detach the handler
SizeChanged -= handler;
};
SizeChanged += handler;
However on the line SizeChanged -= handler I get this error
Use of unassigned local variable 'handler'
Do you have idead on how I should proceed ? I thought about using a boolean flag, but I will do that only if I can't find a way to detach the handler.
The C# compiler will first create the lambda expression you wrote before assigning the result to the variable. So when the lambda is defined, handler doesn't have a value.
It works though if you assign a value of null to EventHandler before.
Since it's a closure and local variables are captured in the closure, at the time of the call handler will have the correct value and it will work:
EventHandler handler=null;
handler = (s, e) =>
{
// Do something
SizeChanged -= handler;
};
SizeChanged += handler;
To all people downvoting: It won't cause a NullReferenceException. handler is a local variable which is captured in the closure, so the value of handler inside the lambda will change, when it changes in the method that contains the closure. I tested it actually on my PC and it works perfectly.
This is because it really is unassigned yet. Try making a named method out of it, so the symbol is known prehand.
private void OnEvent(object sender, EventArgs e)
{
// Do something
AnEvent -= OnEvent;
}
private void RegisterOnce()
{
AnEvent += OnEvent;
}
I would also recommend to run the DoSmething code only after detatch and implement some locking mechanism, in case you have multithrading, to prevent from multiple threads call the event at the exact same time, not having time to detatch and therefore, all run.

Would the following pattern of unsubscribing your self from an event via closure cause any problems?

Consider the situation in which you want to subscribe to an event for one and only one notification. Once the first notification lands, you unsubscribe from all future events. Would the following pattern present any memory issues? It works, but I wasn't sure if the self-referencing closure could keeps things around in memory longer than desired.
public class Entity
{
public event EventHandler NotifyEvent;
}
// And then, elsewhere, for a listen-once handler, we might do this:
Entity entity = new Entity();
Action<object, EventArgs> listener = null;
listener = (sender, args) =>
{
// do something interesting
// unsubscribe, so we only get 1 event notification
entity.NotifyEvent -= new EventHandler(listener);
};
entity.NotifyEvent += new EventHandler(listener);
Note that you have to declare 'listener' and assign a value (null). Otherwise the compiler complains about 'Use of unassigned local variable listener'
There is nothing wrong with this pattern. It's the very same pattern I and many others use for assigning and removing a lambda expression to an event handler.
While I think the general pattern is fine, I wouldn't go through Action<object, EventArgs>. I'd use:
EventHandler listener = null;
listener = (sender, args) =>
{
// do something interesting
// unsubscribe, so we only get 1 event notification
entity.NotifyEvent -= listener;
};
entity.NotifyEvent += listener;

Categories