Обработчик событий c# вызывается несколько раз, когда событие возникает один раз

Ниже приведен мой код: во-первых, я вызываю событие, а во-вторых, я использую его в другом классе. Это кажется довольно простым, но журналы показывают, что даже если событие возникает один раз, оно срабатывает более 20 раз в классе, который использует это событие. Любые идеи?

IBSerialPort класс:

public delegate void PacketReceivedHandler(object sender, PacketReceivedEventArgs e);
public event PacketReceivedHandler OnPacketReceived;

public class PacketReceivedEventArgs : EventArgs
{
  public Packet PacketReceived { get; private set; }

  public PacketReceivedEventArgs(Packet packet)
  {
    PacketReceived = packet;
  }
}

// raise event
if (OnPacketReceived != null)
{
    Log("This is only called ONCE!");
    PacketReceivedEventArgs args = new PacketReceivedEventArgs(data);
    OnPacketReceived(this, args);
}

Класс, который использует IBSerialPort и использует свое событие OnPacketReceived:

IBSerialPort ibSerialPort = null;
..
if (ibSerialPort == null)
{
  Log("This is only called once");

  ibSerialPort = IBSerialPort.Instance;

  ibSerialPort.OnPacketReceived += ibSerialPort_OnPacketReceived;
}

void ibSerialPort_OnPacketReceived(object sender, IBSerialPort.PacketReceivedEventArgs args)
{
   Log("This is called ~25 times!!!!");
}

person Jason Smith    schedule 07.08.2014    source источник
comment
Вы уверены, что подписываетесь на событие только один раз?   -  person Tyler    schedule 07.08.2014
comment
Куда ты звонишь ibSerialPort.OnPacketReceived += ibSerialPort_OnPacketReceived   -  person Yuval Itzchakov    schedule 07.08.2014
comment
Имейте в виду, что даже если вы считаете, что ваш подписчик освобожден, для подписки будет сохранена ссылка.   -  person payo    schedule 07.08.2014


Ответы (4)


Попробуйте это, это отменит регистрацию любого предыдущего подписчика:

ibSerialPort.OnPacketReceived -= ibSerialPort_OnPacketReceived;   // unregister
ibSerialPort.OnPacketReceived += ibSerialPort_OnPacketReceived;  //register
person T McKeown    schedule 07.08.2014
comment
да, если это код метода, то он будет перерегистрировать событие при каждом вызове. - person T McKeown; 07.08.2014
comment
ibSerialPort — это объявление экземпляра. Объект COM использует этот класс, интересно, ibSerialPort.OnPacketReceived += ibSerialPort_OnPacketReceived; вызывается несколько раз. Спасибо за быстрые ответы! - person Jason Smith; 07.08.2014
comment
Хороший ответ, как и мой. - person Rick S; 07.08.2014
comment
Я вас не копировал, уверяю вас, это довольно распространенная техника. - person T McKeown; 07.08.2014
comment
Не обвиняя в краже, просто говорю... отличный ответ! - person Rick S; 07.08.2014

Сколько раз это вызывается? Если это вызывается несколько раз, ваше событие будет вызываться несколько раз.

 ibSerialPort.OnPacketReceived += ibSerialPort_OnPacketReceived;

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

ibSerialPort.OnPacketReceived -= ibSerialPort_OnPacketReceived;
ibSerialPort.OnPacketReceived += ibSerialPort_OnPacketReceived;
person Rick S    schedule 07.08.2014
comment
это буквально сработало для меня как шарм ... не заметил, что я продолжал добавлять все больше и больше событий в элемент управления - person Bernard Walters; 12.01.2017

Интересно, используется ли ваш класс, который определяет ibSerialPort_OnPacketReceived (даже в отдельных экземплярах) 25 раз, и вы думаете, что выпускаете его. Рассмотрим этот код:

class EventSender
{
    public Action MyEvent;
}

class Subscriber
{
    public void OnEvent()
    {
        Console.WriteLine("OnEvent");
    }
}

class Program
{
    static void Main(string[] args)
    {
        EventSender es = new EventSender();

        Subscriber s = new Subscriber();
        es.MyEvent += s.OnEvent;

        s = new Subscriber();
        es.MyEvent += s.OnEvent;

        es.MyEvent();

        Console.ReadKey();
    }
}

Здесь «OnEvent» будет напечатано дважды. Ссылка на подписку сохраняется, хотя кажется, что я освободил ее дескриптор. Это связано с тем, как делегаты ведут список своих подписчиков.

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

es.MyEvent -= s.OnEvent

Это должно быть сделано до того, как вы потеряете свой дескриптор для своего подписчика (то есть до того, как s выйдет за рамки или null). Вы можете отслеживать источник событий в подписчике и иметь метод Dispose, который отменяет подписку для вас.

Кроме того, как уже отмечали другие, вы можете отказаться от подписки перед подпиской :) Я уверен, что к настоящему времени у вас есть нужное вам решение.

person payo    schedule 07.08.2014

У меня была такая же проблема, зарегистрировать свое событие в синхронном методе (я положил его в form_loaded)

    private async void Window_Loaded(object sender, RoutedEventArgs e)
    {
        RefreshHierarchy.COIDConflict += RefreshHierarchy_COIDConflict;
    }
person PigSpider    schedule 27.03.2018