Which C# pattern has better performance to avoid duplicated event handlers?
Asked Answered
D

4

13

There are basically two patterns in avoiding duplicated registering of event handlers: (According to this discussion: C# pattern to prevent an event handler hooked twice)

  1. Using System.Linq namespace, and check if the event handler is registered by calling GetInvocationList().Contains(MyEventHandlerMethod);

  2. Do the unregistering before registering, like this:

    MyEvent -= MyEventHandlerMethod;
    MyEvent += MyEventHandlerMethod;
    

My question is, performance-wise, which one is better, or is there a significant difference between them in performance?

Durrell answered 30/12, 2014 at 10:39 Comment(7)
Seriously these operations are very quick and unless you have many many thousands of event handlers there should be no practical benefit in this kind of performance optimization. Do you have an actual situation where this is an issue?Kilderkin
You're talking about nano seconds, there is no difference just a condition check, you need million of operations to see such different.Psychodynamics
I can't give a clear answer but I would assume the Contains() because +=/-= would internally still need to loop through the invocation list and then manipulate it twice. But as Enigmativity says, you aren't likely to get in a situation where it will make any differenceArtema
@Artema Linq has it's overheads too, and generally you can easily see performance hits in tight loops, where linq iterates over small collections. But there probably is no way of telling which is faster with out a proper benchmark.Thatcher
Measure and know. Do not guess nor ask for guesses. Worry about the rest of your code first, chances are there a bigger/worse/more difficult battles with higher returns on investment to be fought.Metastasis
The .NET Framework is heavily invested in defeating "patterns" like this. By giving events add/remove accessors so you can't get the invocation list. Primarily because it is not a pattern at all, it is a bug. Intentionally hiding bugs in the client code or double-guessing at what the client code intended when it subscribes events is a bug.Hasseman
If (1) was faster the C# compiler would simply emit (1) if you write (2).Rainbow
H
2

According the documentation, invocation list is being stored as array or something similar to it, and the order of the event handlers is being stored too. May be there are inner structure to maintain fast search for a particular method there.

So in the worst case operation of the GetInvocationList().Contains(MyEventHandlerMethod); is O(1) (as we simply got the reference for the array) + O(n) for searching the method, even if there is no optimization for it. I seriously doubt that this is true, and I think that there is some optimizing code, and it is O(log_n).

Second approach has additional operation of adding which is, I think, O(1), as we adding the event handler to the end.

So, to see the difference between such actions, you need a lot of the event handlers.
But! If you use the second approach, as I said, you'll add the event handler to the end of the queue, which can be wrong in some cases. So use the first one, and has no doubt in it.

Heartstricken answered 30/12, 2014 at 10:50 Comment(0)
F
5

I don't think this matters a lot, both in assumed performance gain and actual difference.

Both GetInvocationList and -= walk the internal array _invocationList. (See source)

The LINQ extension method Contains will take more time since it needs the entire array to be walked and converted, returned and then checked by Contains itself. The Contains has the advantage it doesn't need to add the event handler if it exists which will mean some performance gain.

Foscalina answered 30/12, 2014 at 10:49 Comment(0)
H
4
  1. won't work for external callers, and is not very efficient anyway
  2. should be fine (note that it creates 2 delegate instances each time, though), however also consider
  3. in most scenarios, it should be easy to know whether you are already subscribed; if you can't know, then that suggests an architectural problem

The typical usage would be "subscribe {some usage} [unsubscribe]" where the unsubscribe may not be necessary, depending on the relative lifetimes of the event publisher and subscriber; if you actually have a re-entrant scenario, then "subscribe if not already subscribed" is itself problematic, because when unsubscribing later, you don't know if you're preventing an outer iteration receiving the event.

Honeywell answered 30/12, 2014 at 10:55 Comment(1)
As a precision, in many cases, you can add the handler in code that should run only once (constructor, Load handler) and remove it in Dispose method...Hearst
H
2

According the documentation, invocation list is being stored as array or something similar to it, and the order of the event handlers is being stored too. May be there are inner structure to maintain fast search for a particular method there.

So in the worst case operation of the GetInvocationList().Contains(MyEventHandlerMethod); is O(1) (as we simply got the reference for the array) + O(n) for searching the method, even if there is no optimization for it. I seriously doubt that this is true, and I think that there is some optimizing code, and it is O(log_n).

Second approach has additional operation of adding which is, I think, O(1), as we adding the event handler to the end.

So, to see the difference between such actions, you need a lot of the event handlers.
But! If you use the second approach, as I said, you'll add the event handler to the end of the queue, which can be wrong in some cases. So use the first one, and has no doubt in it.

Heartstricken answered 30/12, 2014 at 10:50 Comment(0)
A
2

MyEvent -= MyEventHandlerMethodfirst need to find the registered event handler in the invocation list in order to remove it. So GetInvocationList().Contains is better, but it's truely insignificant.

But, notice that you can't access event EventHandler foo's invocation list....

Aviator answered 30/12, 2014 at 10:51 Comment(1)
"notice that you can't access event EventHandler foo's invocation list" > you can if the object owns the event handler.Foscalina

© 2022 - 2024 — McMap. All rights reserved.