Предотвращение одного назначения обработчика события несколько раз

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

object.Event += MyFunction

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

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

object.Event -= MyFunction; 

object.Event += MyFunction;

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

Ответ 1

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

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;
    }
}

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

Ответ 2

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

Ответ 3

Вы можете реализовать собственное хранилище делгатов и проверить уникальность при добавлении их в событие. См. Ниже пример 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 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);
            }
        }
    }
}

Ответ 4

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

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

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

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