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

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

object.Event += MyFunction

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

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

object.Event -= MyFunction; 

object.Event += MyFunction;

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

Ваше решение сначала удалить событие, а затем добавить его, на самом деле является высоко оцененным ответом на stackoverflow.com/questions/937181/… (у этого вопроса также есть другие ответы на эту тему)

OneWorld 08.02.2013 12:46

возможный дубликат Как обеспечить подписку на событие только один раз

Ian Ringrose 30.12.2014 15:55
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
45
2
19 746
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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

Ответ принят как подходящий

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

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

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

Чтобы избежать ошибки компилятора, при запуске события используйте «частное» имя Event. 'foo' против 'Foo'

Zyo 29.06.2014 00:09

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

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

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

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

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

Другие вопросы по теме