No-op subscriber to events in C# - null checking redundant - c#

So if I do something like this on my event declaration:
public event MyDelegate MyEvent = delegate { };
I read that this makes null checks redundant, that there will be a no-op subscriber.
I'm curious how this works under the hood. What exactly happens and is this a good practice, or should I stick with usual null check before firing the event handler?

The null checks aren't tricky because of an extra if - they are tricky because they're unreliable in a multi-threaded environment.
It's a trade-off. If you expect that the event will only have 0-1 delegates, using a (safe) null-check is probably better. If you expect to have multiple subscribers that keep subscribing and unsubscribing, you're better off with the "null-delegate". It does mean an extra delegate in the call chain, but that's usually cheap enough for most uses of events in .NET.
You wouldn't want to use it on e.g. a control that has 50 different events most of which never have a subscriber, or your behaviour changes based on whether there is a subscriber or not.
As for what happens under the hood, well... nothing. This does no magic whatsoever. The trick is in how += works on events / delegates.
If you have a null event, and use +=, it simply assigns a new delegate to the event. If there already is a delegate, it will create a new delegate, which is a combination of the previous delegate and the new one. If you use that null-delegate, it simply means that there is always some delegate to combine. The usual code for invoking an event can look something like this:
var ev = Click;
if (ev != null) ev();
This means that if there is no subscriber to the event, there is no invocation. In the null-delegate case, the code is further simplified to
Click();
This always means a delegate invocation - at the very least, your null-delegate is invoked. Sure, it doesn't actually do anything, but you keep the delegate invocation overhead. Again, this is rarely a problem, but there are cases where this is unacceptable.

Related

Where should events be raised?

I'm not new to C#, but events is one of the most confusing topics I confront in the language.
one of the questions is: where should I assign the event handler to the event, or the question in other form: why most events are raised in the subscriber constructor? what does it mean?
like this example (taken from the book Mastering Visual C# )
public ReactorMonitor(Reactor myReactor)
{
myReactor.OnMeltdown +=
new Reactor.MeltdownHandler(DisplayMessage);
}
Raise = generate. Events are not raised in the subscriber constructor. The subscriber does not raise events at all. The event source raises/generates events, and subscribers subscribe to, receive, and handle them.
Events in c# are nothing more than function pointers, i.e. a variable that contains a pointer (or list of pointers) to a function that matches a specific signature (typically Action<object,EventArgs>). Or in the words of this MSDN article:
Delegates are like C++ function pointers but are type safe.
When you subscribe to an event, you are essentially saying "Store the address of my function. When X happens, please call it (along with any other function whose address is stored)."
Thus the code
myReactor.OnMeltdown += Meldownhandler;
can be read as
objectThatRaisesEvents.FunctionPointer = MyHandler;
Notice that MyHandler is not followed by parentheses. If it were MyHandler() that means you are invoking the function, the value of the expression MyHandler() is actually the return value of the function; MyHandler by itself doesn't invoke the function or return its value but instead returns the address of the handler itself. So the above line of code takes the address of MyHandler and stores it in a variable named FunctionPointer.
When the object that raises events invokes FunctionPointer() it is telling c# to obtain the address of the function stored in FunctionPointer and invoke it.
So it is really calling MyHandler() indirectly. Thus these two lines do exactly the same thing:
objectThatRaisesEvents.FunctionPointer();
MyHandler();
Also notice the += in your example. In c# that is the equivalent of
objectThatRaisesEvents.FunctionPointer =
objectThatRaisesEvents.FunctionPointer + MyHandler;
We typically use that syntax because there might be several handlers that subscribe to an event. += has the effect of appending our handler to the list. You could use = instead, but it would unsubscribe any existing handlers.
To answer your question-- when should you subscribe to events? That is a very broad question, but typically
In an ASP.NET web page, you'd subscribe during the Init event.
In a Windows Form, you'd subscribe in the InitializeComponent method.
There are many other contexts of course... the bottom line is you should subscribe before you need to receive notifications of occurrences of the event.
The event is one of the standard features of .NET Framework built using delegate model implementing the Observer design pattern.
https://msdn.microsoft.com/en-us/library/edzehd2t(v=vs.110).aspx
https://en.m.wikipedia.org/wiki/Observer_pattern
Where you subscribe to the event depends on the business rules, but most of the time you want to subscribe at the earliest possible moment which is usually the constructor of the class interested in the handling of the event.
When events should be raised on the other hand depends on what you are trying to communicate to the subscribers if it is a notification of the state change then you raise an event as soon as state is changed.
An event is a message sent by an object to signal the occurrence of an
action. The action could be caused by user interaction, such as a
button click, or it could be raised by some other program logic, such
as changing a property’s value.

Is this a better way to fire/invoke events without a null check in C#?

Most code I have seen uses the following way to declare and invoke event firing:
public class MyExample
{
public event Action MyEvent; // could be an event EventHandler<EventArgs>, too
private void OnMyEvent()
{
var handler = this.MyEvent; // copy before access (to aviod race cond.)
if (handler != null)
{
handler();
}
}
public void DoSomeThingsAndFireEvent()
{
// ... doing some things here
OnMyEvent();
}
}
Even ReSharper generates an invoking method the way mentioned above.
Why not just do it this way:
public class MyExample
{
public event Action MyEvent = delegate {}; // init here, so it's never null
public void DoSomeThingsAndFireEvent()
{
// ... doing some things here
OnMyEvent(); // save to call directly because this can't be null
}
}
Can anyone explain a reason why not to do this? (pro vs. cons)
The pros and cons are:
null checks are extremely cheap; we're talking billionths of a second. Allocating a delegate and then garbage collecting it unsuccessfully for the rest of the lifetime of the object could take maybe upwards of a few millionths of a second. Plus, you're consuming dozens of bytes more memory. Plus, every time the event fires, you get an unnecessary call to a method that does nothing, consuming even more microseconds. If you're the sort of person who cares about millionths of a second and dozens of bytes then that might be a meaningful difference; for the vast majority of cases it will not.
You have to remember to always create the empty delegate. Is that really any easier than remembering to check for null?
Neither pattern actually makes the event threadsafe. It is still entirely possible with both patterns for an event handler to be fired on one thread while being removed on another thread, and that means that they race. If your handler removal code destroys state that the handler needs, then it is possible that one thread is destroying that state while another thread is running the handler. Do not think that merely checking for null or assigning an empty handler magically eliminates race conditions. It only eliminates the race condition that results in dereferencing null.
It is a style thing for sure; however, I think most developers go for safety over style when it comes to null checks. There is nothing in this world that can guarantee that a bug will not creep into the system and that null check will continue to be unnecessary.
It can still be null. Consider:
var x = new MyExample();
x.MyEvent += SomeHandler;
// ... later, when the above code is disposed of
x.MyEvent -= SomeHandler;
EDIT: actually, I take it back. Having tested this, if you've used an anonymous delegate to set the handler, it doesn't look like you can clear it, so you can save the null check.
I'm not sure whether this is reliable behaviour though, or just an artifact of the language implementation...
The best tradeoff I've seen for simplifying event firing is the addition of an extension method. See Raising C# events with an extension method - is it bad?.

How does C# eventhandler work internally?

I guess C# eventhandler has a list of listeners and it loops thru the list when sending a message. My question is how does this works in internally. Does it make a copy of the list before looping it thru and if so, what happens if someone unregister itself after the list has been copied but it has not yet received the message.
Will it still get the message even do it has unregister itself?
A delegate is immutable, so when you are invoking a delegate the list of subscribers is known and fixed. Subscribing or unsubscribing replaces the delegate that underpins the event.
This does indeed mean that in a multi-threaded scenario you can receive an event after unsubscribing, because either:
the delegate was already in the process of being invoked
a snapshot of the delegate had already been obtained for the purpose of invoking
by 2, I mean the usual pattern (to prevent a null-ref during invoke):
var handler = SomeEvent;
// <===== another thread could unsubscribe at this point
if(handler != null) handler(sender, args); // <== or part way through this invoke
// (and it either case, have the event trigger even though they think they have
// unsubscribed)
For that reason, if you are coding complex multi-threaded code with events, you should code defensively such that the event firing after you think you have unsubscribed is not a problem.
These nuances do not really impact single-threaded code.

How to implement a thread safe error-free event handler in C#?

Problem background
An event can have multiple subscribers (i.e. multiple handlers may be called when an event is raised). Since any one of the handlers could throw an error, and that would prevent the rest of them from being called, I want to ignore any errors thrown from each individual handler. In other words, I do not want an error in one handler to disrupt the execution of other handlers in the invocation list, since neither those other handlers nor the event publisher has any control over what any particular event handler's code does.
This can be accomplished easily with code like this:
public event EventHandler MyEvent;
public void RaiseEventSafely( object sender, EventArgs e )
{
foreach(EventHandlerType handler in MyEvent.GetInvocationList())
try {handler( sender, e );}catch{}
}
A generic, thread-safe, error-free solution
Of course, I don't want to write all this generic code over and over every time I call an event, so I wanted to encapsulate it in a generic class. Furthermore, I'd actually need additional code to ensure thread-safety so that MyEvent's invocation list does not change while the list of methods is being executed.
I decided to implement this as a generic class where the generic type is constrained by the "where" clause to be a Delegate. I really wanted the constraint to be "delegate" or "event", but those are not valid, so using Delegate as a base class constraint is the best I can do. I then create a lock object and lock it in a public event's add and remove methods, which alter a private delegate variable called "event_handlers".
public class SafeEventHandler<EventType> where EventType:Delegate
{
private object collection_lock = new object();
private EventType event_handlers;
public SafeEventHandler(){}
public event EventType Handlers
{
add {lock(collection_lock){event_handlers += value;}}
remove {lock(collection_lock){event_handlers -= value;}}
}
public void RaiseEventSafely( EventType event_delegate, object[] args )
{
lock (collection_lock)
foreach (Delegate handler in event_delegate.GetInvocationList())
try {handler.DynamicInvoke( args );}catch{}
}
}
Compiler issue with += operator, but two easy workarounds
One problem ran into is that the line "event_handlers += value;" results in the compiler error "Operator '+=' cannot be applied to types 'EventType' and 'EventType'". Even though EventType is constrained to be a Delegate type, it will not allow the += operator on it.
As a workaround, I just added the event keyword to "event_handlers", so the definition looks like this "private event EventType event_handlers;", and that compiles fine. But I also figured that since the "event" keyword can generate code to handle this, that I should be able to as well, so I eventually changed it to this to avoid the compiler's inability to recognize that '+=' SHOULD apply to a generic type constrained to be a Delegate. The private variable "event_handlers" is now typed as Delegate instead of the generic EventType, and the add/remove methods follow this pattern event_handlers = MulticastDelegate.Combine( event_handlers, value );
The final code looks like this:
public class SafeEventHandler<EventType> where EventType:Delegate
{
private object collection_lock = new object();
private Delegate event_handlers;
public SafeEventHandler(){}
public event EventType Handlers
{
add {lock(collection_lock){event_handlers = Delegate.Combine( event_handlers, value );}}
remove {lock(collection_lock){event_handlers = Delegate.Remove( event_handlers, value );}}
}
public void RaiseEventSafely( EventType event_delegate, object[] args )
{
lock (collection_lock)
foreach (Delegate handler in event_delegate.GetInvocationList())
try {handler.DynamicInvoke( args );}catch{}
}
}
The Question
My question is... does this appear to do the job well? Is there a better way or is this basically the way it must be done? I think I've exhausted all the options. Using a lock in the add/remove methods of a public event (backed by a private delegate) and also using the same lock while executing the invocation list is the only way I can see to make the invocation list thread-safe, while also ensuring errors thrown by handlers don't interfere with the invocation of other handlers.
Since any one of the handlers could throw an error, and that would prevent the rest of them from being called,
You say that like it is a bad thing. That is a very good thing. When an unhandled, unexpected exception is thrown that means that the entire process is now in an unknown, unpredictable, possibly dangerously unstable state.
Running more code at this point is likely to make things worse, not better. The safest thing to do when this happens is to detect the situation and cause a failfast that takes down the entire process without running any more code. You don't know what awful thing running more code is going to do at this point.
I want to ignore any errors thrown from each individual handler.
This is a super dangerous idea. Those exceptions are telling you that something awful is happening, and you're ignoring them.
In other words, I do not want an error in one handler to disrupt the execution of other handlers in the invocation list, since neither those other handlers nor the event publisher has any control over what any particular event handler's code does.
Whose in charge here? Someone is adding those event handlers to this event. That is the code that is responsible for ensuring that the event handlers do the right thing should there be an exceptional situation.
I then create a lock object and lock it in a public event's add and remove methods, which alter a private delegate variable called "event_handlers".
Sure, that's fine. I question the necessity of the feature -- I very rarely have a situation where multiple threads are adding event handlers to an event -- but I'll take your word for it that you are in this situation.
But in that scenario this code is very, very, very dangerous:
lock (collection_lock)
foreach (Delegate handler in event_delegate.GetInvocationList())
try {handler.DynamicInvoke( args );}catch{}
Let's think about what goes wrong there.
Thread Alpha enters the collection lock.
Suppose there is another resource, foo, which is also controlled by a different lock. Thread Beta enters the foo lock in order to obtain some data that it needs.
Thread Beta then takes that data and attempts to enter the collection lock, because it wants to use the contents of foo in an event handler.
Thread Beta is now waiting on thread Alpha. Thread Alpha now calls a delegate, which decides that it wants to access foo. So it waits on thread Beta, and now we have a deadlock.
But can't we avoid this by ordering the locks? No, because the very premise of your scenario is that you don't know what the event handlers are doing! If you already know that the event handlers are well-behaved with respect to their lock ordering then you can presumably also know that they are well-behaved with respect to not throwing exceptions, and the whole problem vanishes.
OK, so let's suppose that you do this instead:
Delegate copy;
lock (collection_lock)
copy = event_delegate;
foreach (Delegate handler in copy.GetInvocationList())
try {handler.DynamicInvoke( args );}catch{}
Delegates are immutable and copied atomically by reference, so you now know that you're going to be invoking the contents of event_delegate but not holding the lock during the invocation. Does that help?
Not really. You've traded one problem for another one:
Thread Alpha takes the lock and makes a copy of the delegate list, and leaves the lock.
Thread Beta takes the lock, removes event handler X from the list, and destroys state necessary to prevent X from deadlocking.
Thread Alpha takes over again and starts up X from the copy. Because Beta just destroyed state necessary for the correct execution of X, X deadlocks. And once more, you are deadlocked.
Event handlers are required to not do that; they are required to be robust in the face of their suddenly becoming "stale". It sounds like you are in a scenario where you cannot trust your event handlers to be well-written. That's a horrid situation to be in; you then cannot trust any code to be reliable in the process. You seem to think that there is some level of isolation you can impose on an event handler by catching all its errors and muddling through, but there is not. Event handlers are just code, and they can affect arbitrary global state in the program like any other code.
In short, your solution is generic, but it is not threadsafe and it is not error-free. Rather, it exacerbates threading problems like deadlocks and it turns off safety systems.
You simply cannot abdicate responsibility for ensuring that event handlers are correct, so don't try. Write your event handlers so that they are correct -- so that they order locks correctly and never throw unhandled exceptions.
If they are not correct and end up throwing exceptions then take down the process immediately. Don't keep muddling through trying to run code that is now living in an unstable process.
Based on your comments on other answers it looks like you think that you should be able to take candy from strangers with no ill effects. You cannot, not without a whole lot more isolation. You can't just sign up random code willy-nilly to events in your process and hope for the best. If you have stuff that is unreliable because you're running third party code in your application, you need a managed add-in framework of some sort to provide isolation. Try looking up MEF or MAF.
The lock inside RaiseEventSafely is both unnecessary and dangerous.
It is unnecessary because delegates are immutable. Once you read it, the invokation list you obtained will not change. It doesn't matter if the changes happen while event code runs, or if the changes need to wait until after.
It is dangerous because you're calling external code while holding a lock. This can easily lead to lock order violations and thus deadlocks. Consider an eventhandler that spawns a new thread that tries to modify the event. Boom, deadlock.
The you have an empty catch for exception. That's rarely a good idea, since it silently swallows the exception. At minimum you should log the exception.
Your generic parameter doesn't start with a T. That's a bit confusing IMO.
where EventType:Delegate I don't think this compiles. Delegate is not a valid generic constraint. For some reason the C# specification forbids certain types as a generic constraint, and one of them is Delegate. (no idea why)
Have you looked into the PRISM EventAggregator or MVVMLight Messenger classes? Both of these classes fulfill all your requirements. MVVMLight's Messenger class uses WeakReferences to prevent memory leaks.
Aside from it being a bad idea to swallow exceptions, I suggest you consider not locking while invoking the list of delegates.
You'll need to put a remark in your class's documentation that delegates can be called after having been removed from the event.
The reason I'd do this is because otherwise you risk performance consequences and possibly deadlocks. You're holding a lock while calling into someone else's code. Let's call your internal lock Lock 'A'. If one of the handlers attempts to acquire a private lock 'B', and on a separate thread someone tries to register a handler while holding lock 'B', then one thread holds lock 'A' while trying to acquire 'B' and a different thread holds lock 'B' while trying to acquire lock 'A'. Deadlock.
Third-party libraries like yours are often written with no thread safety to avoid these kinds of issues, and it is up to the clients to protect methods that access internal variables. I think it is reasonable for an event class to provide thread safety, but I think the risk of a 'late' callback is better than a poorly-defined lock hierarchy prone to deadlocking.
Last nit-pick, do you think SafeEventHandler really describes what this class does? It looks like an event registrar and dispatcher to me.
It is a bad practice to swallow exceptions entirely. If you have a use case where you would like a publisher to recover gracefully from an error raised by a subscriber then this calls for the use of an event aggregator.
Moreover, I'm not sure I follow the code in SafeEventHandler.RaiseEventSafely. Why is there an event delegate as a parameter? It seems to have no relationship with the event_handlers field. As far as thread-safety, after the call to GetInvocationList, it does not matter if the original collection of delegates is modified because the array returned won't change.
If you must, I would suggest doing the following instead:
class MyClass
{
event EventHandler myEvent;
public event EventHandler MyEvent
{
add { this.myEvent += value.SwallowException(); }
remove { this.myEvent -= value.SwallowException(); }
}
protected void OnMyEvent(EventArgs args)
{
var e = this.myEvent;
if (e != null)
e(this, args);
}
}
public static class EventHandlerHelper
{
public static EventHandler SwallowException(this EventHandler handler)
{
return (s, args) =>
{
try
{
handler(s, args);
}
catch { }
};
}
}
Juval Löwy provides an implementation of this in his book "Programming .NET components".
http://books.google.com/books?id=m7E4la3JAVcC&lpg=PA129&pg=PA143#v=onepage&q&f=false
I considered everything everyone said, and arrived at the following code for now:
public class SafeEvent<EventDelegateType> where EventDelegateType:class
{
private object collection_lock = new object();
private Delegate event_handlers;
public SafeEvent()
{
if(!typeof(Delegate).IsAssignableFrom( typeof(EventDelegateType) ))
throw new ArgumentException( "Generic parameter must be a delegate type." );
}
public Delegate Handlers
{
get
{
lock (collection_lock)
return (Delegate)event_handlers.Clone();
}
}
public void AddEventHandler( EventDelegateType handler )
{
lock(collection_lock)
event_handlers = Delegate.Combine( event_handlers, handler as Delegate );
}
public void RemoveEventHandler( EventDelegateType handler )
{
lock(collection_lock)
event_handlers = Delegate.Remove( event_handlers, handler as Delegate );
}
public void Raise( object[] args, out List<Exception> errors )
{
lock (collection_lock)
{
errors = null;
foreach (Delegate handler in event_handlers.GetInvocationList())
{
try {handler.DynamicInvoke( args );}
catch (Exception err)
{
if (errors == null)
errors = new List<Exception>();
errors.Add( err );
}
}
}
}
}
This bypasses the compiler's special treatment of the Delegate as an invalid base class. Also, events cannot be typed as Delegate.
Here is how a SafeEvent would be used to create an event in a class:
private SafeEvent<SomeEventHandlerType> a_safe_event = new SafeEvent<SomeEventHandlerType>();
public event SomeEventHandlerType MyEvent
{
add {a_safe_event.AddEventHandler( value );}
remove {a_safe_event.RemoveEventHandler( value );}
}
And here is how the event would be raised and errors handled:
List<Exception> event_handler_errors;
a_safe_event.Raise( new object[] {event_type, disk}, out event_handler_errors );
//Report errors however you want; they should not have occurred; examine logged errors and fix your broken handlers!
To summarize, this component's job is to publish events to a list of subscribers in an atomic manner (i.e. the event will not be re-raised and the invocation list will not be changed while the invocation list is executing). Deadlock is possible but easily avoided by controlling access to the SafeEvent, because a handler would have to spawn a thread that calls one of the public methods of the SafeEvent and then wait on that thread. In any other scenario, other threads would simply block until the lock owning-thread releases the lock. Also, while I do not believe in ignoring errors at all, I also do not believe that this component is in any place to handle subscriber errors intelligently nor make a judgement call about the severity of such errors, so rather than throw them and risk crashing the application, it reports errors to the caller of "Raise", since the caller is likely to be in a better position to handle such errors. With that said, this components provides a kind of stability to events that's lacking in the C# event system.
I think what people are worried about is that letting other subscribers run after an error has occurred means they are running in an unstable context. While that might be true, that means the application is in fact written incorrectly any way you look at it. Crashing is no better a solution than allowing the code to run, because allowing the code to run will allow errors to be reported, and will allow the full effects of the error to be manifest, and this, in turn, will assist engineers to more quickly and thoroughly understand the nature of the error and FIX THEIR CODE FASTER.

Checking if an event handler exists

Following on from this post - what are the disadvantages when using the -= then += approach suggested when I only ever want one handler event to fire?
_value.PropertyChanged -= _handlerMethod;
_value.PropertyChanged += _handlerMethod;
This doesn't guarantee that there is only one handler fired.
Another location could, potentially, subscribe your handler to your event multiple times. In this case, you will only remove the first handler call.
By inspecting the invocation list of the event, you could guarantee this behavior, if you truly only want a single handler to be subscribed at a time.
If you really want only one handler to execute, then you may want to use a settable property of the appropriate delegate type instead of an event. Only one delegate will be stored; you can execute the delegate the same as you would the event handler.
The idea here is that the -= operator is not going to do anything if your event handler is not assigned.
I don't personally like this approach, I think you should really aim to refactor your code so that you know that the event handler is assigned only once.
The disadvantages would be:
- possibility of race condition, if your app is multithreaded and the event gets fired when the handler is not assigned
- I am also not sure what happens when you run _value.PropertyChanged -= _handlerMethod when two copies of the handler are already assigned.
- messy code - obviously its not clear from the code which class and when is listening to the event

Categories

Resources