How to Ensure an Event Is Only Subscribed to Once

How to ensure an event is only subscribed to once

If you are talking about an event on a class that you have access to the source for then you could place the guard in the event definition.

private bool _eventHasSubscribers = false;
private EventHandler<MyDelegateType> _myEvent;

public event EventHandler<MyDelegateType> MyEvent
{
add
{
if (_myEvent == null)
{
_myEvent += value;
}
}
remove
{
_myEvent -= value;
}
}

That would ensure that only one subscriber can subscribe to the event on this instance of the class that provides the event.

EDIT please see comments about why the above code is a bad idea and not thread safe.

If your problem is that a single instance of the client is subscribing more than once (and you need multiple subscribers) then the client code is going to need to handle that. So replace

not already subscribed

with a bool member of the client class that gets set when you subscribe for the event the first time.

Edit (after accepted): Based on the comment from @Glen T (the submitter of the question) the code for the accepted solution he went with is in the client class:

if (alreadySubscribedFlag)
{
member.Event += new MemeberClass.Delegate(handler);
}

Where alreadySubscribedFlag is a member variable in the client class that tracks first subscription to the specific event.
People looking at the first code snippet here, please take note of @Rune's comment - it is not a good idea to change the behavior of subscribing to an event in a non-obvious way.

EDIT 31/7/2009: Please see comments from @Sam Saffron. As I already stated and Sam agrees the first method presented here is not a sensible way to modify the behavior of the event subscription. The consumers of the class need to know about its internal implementation to understand its behavior. Not very nice.

@Sam Saffron also comments about thread safety. I'm assuming that he is referring to the possible race condition where two subscribers (close to) simultaneously attempt to subscribe and they may both end up subscribing. A lock could be used to improve this. If you are planning to change the way event subscription works then I advise that you read about how to make the subscription add/remove properties thread safe.

Ensuring that we subscribe only once to an event

So long as you do that everywhere you're subscribing that handler, it should be fine. But if you subscribe 100 times and then run that code, you're still going to be left with 100 subscriptions.

(I'm assuming you're only using a single thread, by the way. There's a race condition if two threads execute that code at the same time... they could both unsubscribe an then both subscribe, leaving two active subscriptions.)

How to determine if an event is already subscribed

The event keyword was explicitly invented to prevent you from doing what you want to do. It restricts access to the underlying delegate object so nobody can directly mess with the events handler subscriptions that it stores. Events are accessors for a delegate, just like a property is an accessor for a field. A property only permits get and set, an event only permits add and remove.

This keeps your code safe, other code can only remove an event handler if it knows the event handler method and the target object. The C# language puts an extra layer of security in place by not allowing you to name the target object.

And WinForms puts an extra layer of security in place so it becomes difficult even if you use Reflection. It stores delegate instances in an EventHandlerList with a secret "cookie" as the key, you'd have to know the cookie to dig the object out of the list.

Well, don't go there. It is trivial to solve your problem with a bit of code on your end:

private bool mSubscribed;

private void Subscribe(bool enabled)
{
if (!enabled) textBox1.VisibleChanged -= textBox1_VisibleChanged;
else if (!mSubscribed) textBox1.VisibleChanged += textBox1_VisibleChanged;

mSubscribed = enabled;
}

Is it possible to subscribe one control to the same event handler multiple times?

You can't ensure that. You would theoretically be allowed to bind the same event handler to a textbox (or other control) more than once. The only thing that events allow you to do is add a handler and remove a handler—there's no additional means provided to check for existing subscribers. If you don't believe me, Jon Skeet provides the authoritative answer here, and in his article on events.

If you need to ensure that you don't accidentally subscribe a control to the same event twice, you'll need to keep track of it yourself. Honestly, you should never end up in a situation where you don't know what event handlers are subscribed. Not only does this reflect sloppy design, but it probably also means that you aren't taking care to remove your event handlers when they are no longer necessary.

A possible solution is provided in the answers to this question, but I caution you from using something like this blindly. As others have argued, this code is something of an anti-pattern.

How to subscribe to event emitter once?

A call to subscribe returns an instance of Disposable, which has a method dispose.

Or if you are using RxJS 5, dispose has been renamed to unsubscribe (thanks @EricMartinez).

And from the RxJS docs:

...when we're no longer interested in receiving the data as it comes streaming in, we call dispose on our subscription.


Store the result of your call to subscribe and later dispose of the subscription within ngOnDestroy.

RxJS 5:

export class SomeComponent implements OnDestroy {
constructor(public service: Service) {
this.subscription = this.service.someEvent.subscribe((x) => {...});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}

RxJS <5:

export class SomeComponent implements OnDestroy {
constructor(public service: Service) {
this.subscription = this.service.someEvent.subscribe((x) => {...});
}
ngOnDestroy() {
this.subscription.dispose();
}
}

Fire an event when an event is subscribed to

Found the solution based on this thread.

public event EventHandler Inactivity
{
add
{
Inactivity += value;
if (!IsAlreadyCheckingForActivity)
{
StartActivityCheck();
}

}
remove
{
Inactivity -= value;
}
}

"Be careful if your function should be thread-safe, in which case you need a lock in order to ensure it to be synched."

Only fire an event once?

You can use jQuery's one method, which will subscribe to only the first occurrence of an event.

For example:

$('something').one('click', function(e) {
alert('You will only see this once.');
});


Related Topics



Leave a reply



Submit