How to ensure an event is only subscribed to once

前端 未结 7 1713
南旧
南旧 2020-11-27 04:20

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:

相关标签:
7条回答
  • 2020-11-27 04:30

    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.

    0 讨论(0)
  • 2020-11-27 04:31

    U can use Postsharper to write one attribute just once and use it on normal Events. Reuse the code. Code sample is given below.

    [Serializable]
    public class PreventEventHookedTwiceAttribute: EventInterceptionAspect
    {
        private readonly object _lockObject = new object();
        readonly List<Delegate> _delegates = new List<Delegate>();
    
        public override void OnAddHandler(EventInterceptionArgs args)
        {
            lock(_lockObject)
            {
                if(!_delegates.Contains(args.Handler))
                {
                    _delegates.Add(args.Handler);
                    args.ProceedAddHandler();
                }
            }
        }
    
        public override void OnRemoveHandler(EventInterceptionArgs args)
        {
            lock(_lockObject)
            {
                if(_delegates.Contains(args.Handler))
                {
                    _delegates.Remove(args.Handler);
                    args.ProceedRemoveHandler();
                }
            }
        }
    }
    

    Just use it like this.

    [PreventEventHookedTwice]
    public static event Action<string> GoodEvent;
    

    For details look at Implement Postsharp EventInterceptionAspect to prevent an event Handler hooked twice

    0 讨论(0)
  • 2020-11-27 04:31

    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.

    0 讨论(0)
  • 2020-11-27 04:34

    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.

    0 讨论(0)
  • 2020-11-27 04:45

    I'm adding this in all the duplicate questions, just for the record. This pattern worked for me:

    myClass.MyEvent -= MyHandler;
    myClass.MyEvent += MyHandler;
    

    Note that doing this every time you register your handler will ensure that your handler is registered only once.

    0 讨论(0)
  • 2020-11-27 04:51

    I did this recently and I'll just drop it here so it stays:

    private bool subscribed;
    
    if(!subscribed)
    {
        myClass.MyEvent += MyHandler;
        subscribed = true;
    } 
    
    private void MyHandler()
    {
        // Do stuff
        myClass.MyEvent -= MyHandler;
        subscribed = false;
    }
    
    0 讨论(0)
提交回复
热议问题