Предотвращение одного и того же назначения обработчика событий несколько раз - PullRequest
45 голосов
/ 30 декабря 2008

Если я назначаю обработчик событий во время выполнения, и он находится в месте, которое можно вызывать несколько раз, то какова рекомендуемая практика, чтобы предотвратить множественные назначения одного обработчика одному и тому же событию.

object.Event += MyFunction

Добавление этого в точку, которая будет вызываться более одного раза, выполнит обработчик 'n' раз (конечно).

Я прибег к удалению любого предыдущего обработчика, прежде чем пытаться добавить через

object.Event -= MyFunction; 

object.Event += MyFunction;

Это работает, но кажется как-то выключено. Любые предложения по правильной обработке;) этого сценария.

Ответы [ 4 ]

37 голосов
/ 30 декабря 2008

Baget прав в использовании явно реализованного события (хотя есть смесь явной реализации интерфейса и полного синтаксиса события). Вы, вероятно, можете сойти с рук:

private EventHandler foo;

public event EventHandler Foo
{
    add
    {
        // First try to remove the handler, then re-add it
        foo -= value;
        foo += value;
    }
    remove
    {
        foo -= value;
    }
}

Это может иметь некоторые странные крайние случаи, если вы когда-либо добавляете или удаляете многоадресных делегатов, но это маловероятно. Также требуется тщательная документация, так как события обычно не работают.

4 голосов
/ 30 декабря 2008

Я склонен добавлять обработчик событий в путь, который выполняется один раз, например в конструкторе.

2 голосов
/ 30 декабря 2008

Вы можете реализовать собственное хранилище delgates и проверять уникальность при добавлении их в событие. См. Ниже класс EventOwner2 для примера. Я не знаю, как это влияет на производительность, но, опять же, это не всегда проблема.

using System;
using System.Collections.Generic;

namespace EventExperiment
{
    class Program
    {
        static void Main(string[] args)
        {
            IEventOwner e=new EventOwner2();
            Subscriber s=new Subscriber(e);
            e.RaiseSome();
            Console.ReadKey();
        }
    }

    /// <summary>
    /// A consumer class, subscribing twice to the event in it's constructor.
    /// </summary>
    public class Subscriber
    {
        public Subscriber(IEventOwner eventOwner)
        {
            eventOwner.SomeEvent += eventOwner_SomeEvent;
            eventOwner.SomeEvent += eventOwner_SomeEvent;
        }

        void eventOwner_SomeEvent(object sender, EventArgs e)
        {
            Console.WriteLine(DateTimeOffset.Now);
        }

    }

    /// <summary>
    /// This interface is not essensial to this point. it is just added for conveniance.
    /// </summary>
    public interface IEventOwner
    {
        event EventHandler<EventArgs> SomeEvent;
        void RaiseSome();
    }

    /// <summary>
    /// A traditional event. This is raised for each subscription.
    /// </summary>
    public class EventOwner1 : IEventOwner
    {
        public event EventHandler<EventArgs> SomeEvent = delegate { };
        public void RaiseSome()
        {
            SomeEvent(this,new EventArgs());
        }
    }
    /// <summary>
    /// A custom event. This is raised only once for each subscriber.
    /// </summary>
    public class EventOwner2 : IEventOwner
    {
        private readonly List<EventHandler<EventArgs>> handlers=new List<EventHandler<EventArgs>>();
        public event EventHandler<EventArgs> SomeEvent
        {
            add
            {
                lock (handlers)
                    if (handlers!=null&&!handlers.Contains(value))
                    {
                        handlers.Add(value);
                    }
            }
            remove
            {
                handlers.Remove(value);
            }
        }
        public void RaiseSome()
        {
            EventArgs args=new EventArgs();
            lock(handlers)
            foreach (EventHandler<EventArgs> handler in handlers)
            {
                handler(this,args);
            }
        }
    }
}
0 голосов
/ 30 декабря 2008

Что такое модификатор доступа для объекта?

Если это личное, вам нужно беспокоиться только о том, какой объект содержит обработчик событий. Если это внутреннее, вам нужно беспокоиться только о сборке, содержащей установку обработчика событий. Если он публичный, то он широко открыт.

Если «объект» можно сделать закрытым для содержащего класса, вы можете сделать свои проверки намного более эффективными, управляя назначением обработчика событий в локальном классе.

Если «внутренние» или «общедоступные» и уникальность требуются, используйте класс-оболочку, который скрывает «объект» и вместо этого предоставляет метод для назначения обработчика событий с вашими проверками за ним, чтобы обеспечить уникальность.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...