views:

796

answers:

4

I would like to ensure that I only subscribe once in a particular class for an event on an instance.

For example I would like to be able to do the following:

if (*not already subscribed*)
{
    member.Event += new MemeberClass.Delegate(handler);
}

How would I go about implementing such a guard?

+2  A: 

You would either need to store a separate flag indicating whether or not you'd subscribed or, if you have control over MemberClass, provide implementations of the add and remove methods for the event:

class MemberClass
{
        private EventHandler _event;

        public event EventHandler Event
        {
            add
            {
                if( /* handler not already added */ )
                {
                    _event+= value;
                }
            }
            remove
            {
                _event-= value;
            }
        }
}

To decide whether or not the handler has been added you'll need to compare the Delegates returned from GetInvocationList() on both _event and value.

Andrew Kennan
+6  A: 

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.

Hamish Smith
I think I'll go ahead with the boolean member variable approach.However I'm a little surprised that there isn't another way to check if a client is already subscribed. I would have thought it was more common for a given client to only want to subscribe once?
Glen T
Depending on your setup you may want to throw an exception if the event already has a subscriber. If you are adding subscribers at run time this will notify them of the error instead of doing nothing. Changing the default behaviour wihtout notifying the user isn't the best practice.
Rune Grimstad
This is not safe for multithreading.
Sam Saffron
Also this is a major anti-pattern, arbitrary consumers need to be privy to your event implementation to understand its behavior.
Sam Saffron
@Sam Saffron: thanks for the comments. Have attempted to place some (further) warnings about the first method into the answer.
Hamish Smith
+3  A: 

As others have shown, you can override the add/remove properties of the event. Alternatively, you may want to ditch the event and simply have the class take a delegate as an argument in its constructor (or some other method), and instead of firing the event, call the supplied delegate.

Events imply that anyone can subscribe to them, whereas a delegate is one method you can pass to the class. Will probably be less surprising to the user of your library then, if you only use events when you actually wnat the one-to-many semantics it usually offers.

jalf
A: 

Just to add: if you don't have control over the class exposing the event, you can wrap it using the facade pattern and accomplish the same thing.

Brian Rasmussen