Шаблон C# для предотвращения повторного подключения обработчика событий [дубликат]



этот вопрос уже есть ответ здесь:



копия: как убедиться, что событие подписано только один раз
и был ли уже добавлен обработчик событий?



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

626   9  

9 ответов:

явно реализовать событие и проверить список вызовов. Вам также нужно будет проверить значение null:

using System.Linq; // Required for the .Contains call below:

...

private EventHandler foo;
public event EventHandler Foo
{
    add
    {
        if (foo == null || !foo.GetInvocationList().Contains(value))
        {
            foo += value;
        }
    }
    remove
    {
        foo -= value;
    }
}

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

Как насчет просто удаления события сначала с -= , если он не найден исключение не выдается

/// -= Removes the event if it has been already added, this prevents multiple firing of the event
((System.Windows.Forms.WebBrowser)sender).Document.Click -= new System.Windows.Forms.HtmlElementEventHandler(testii);
((System.Windows.Forms.WebBrowser)sender).Document.Click += new System.Windows.Forms.HtmlElementEventHandler(testii);

я протестировал каждое решение, и лучшее из них (учитывая производительность):

private EventHandler _foo;
public event EventHandler Foo {

    add {
        _foo -= value;
        _foo += value;
    }
    remove {
        _foo -= value;
    }
}

нет LINQ, которая, используя необходимое. Нет необходимости проверять значение null перед отменой подписки (см. MS EventHandler для получения дополнительной информации). Нет необходимости помнить, чтобы сделать отписку везде.

вы действительно должны справиться с этим на уровне раковины, а не на уровне источника. То есть, не предписывайте логику обработчика событий в источнике событий - оставьте это самим обработчикам (приемникам).

как разработчик сервиса, кто вы такой, чтобы сказать, что приемники могут регистрироваться только один раз? Что делать, если они хотят зарегистрироваться дважды по какой-то причине? И если вы пытаетесь исправить ошибки в приемниках, изменив источник, это снова хорошая причина для исправления этих проблем на на уровне раковины.

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

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

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

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

вот пример кода, скомпилировать как консольное приложение:

using System;
using System.Linq;

namespace DemoApp
{
    public class TestClass
    {
        private EventHandler _Test;

        public event EventHandler Test
        {
            add
            {
                if (_Test == null || !_Test.GetInvocationList().Contains(value))
                    _Test += value;
            }

            remove
            {
                _Test -= value;
            }
        }

        public void OnTest()
        {
            if (_Test != null)
                _Test(this, EventArgs.Empty);
        }
    }

    class Program
    {
        static void Main()
        {
            TestClass tc = new TestClass();
            tc.Test += tc_Test;
            tc.Test += tc_Test;
            tc.OnTest();
            Console.In.ReadLine();
        }

        static void tc_Test(object sender, EventArgs e)
        {
            Console.Out.WriteLine("tc_Test called");
        }
    }
}

выход:

tc_Test called

(т. е. только один раз)

Microsoft реактивные расширения (Rx) framework также можно использовать, чтобы сделать"подписаться только один раз".

учитывая событие мыши foo.Нажал, вот как подписаться и получить только один вызов:

Observable.FromEvent<MouseEventArgs>(foo, "Clicked")
    .Take(1)
    .Subscribe(MyHandler);

...

private void MyHandler(IEvent<MouseEventArgs> eventInfo)
{
   // This will be called just once!
   var sender = eventInfo.Sender;
   var args = eventInfo.EventArgs;
}

в дополнение к предоставлению функции" подписаться один раз", подход RX предлагает возможность создавать события вместе или фильтровать события. Это довольно изящно.

создать действие вместо события. Ваш класс может выглядеть так:

public class MyClass
{
                // sender   arguments       <-----     Use this action instead of an event
     public Action<object, EventArgs> OnSomeEventOccured;

     public void SomeMethod()
     {
          if(OnSomeEventOccured!=null)
              OnSomeEventOccured(this, null);
     }

}

У вашего объекта singleton проверьте, что это список тех, кого он уведомляет, и только один раз позвоните, если он дублируется. Альтернативно, если это возможно, отклонить запрос на вложение события.

в silverlight вам нужно сказать e. Handled = true; в коде события.

void image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    e.Handled = true; //this fixes the double event fire problem.
    string name = (e.OriginalSource as Image).Tag.ToString();
    DoSomething(name);
}

пожалуйста, отметьте меня, если это помогает.

Comments

    Ничего не найдено.