当事件被触发一次时,c#事件处理程序会被多次调用

本文关键字:事件处理 调用 程序 事件 一次 | 更新日期: 2023-09-27 18:17:04

下面是我的代码,首先是我引发事件的地方,第二部分是我在另一个类中使用它的地方。这看起来非常直接,但是日志显示,即使事件被引发一次,事件在使用该事件的类上触发20多次。什么好主意吗?

IBSerialPort class:

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!!!!");
}

当事件被触发一次时,c#事件处理程序会被多次调用

试试这个,这将取消所有以前的订阅者:

ibSerialPort.OnPacketReceived -= ibSerialPort_OnPacketReceived;   // unregister
ibSerialPort.OnPacketReceived += ibSerialPort_OnPacketReceived;  //register

这个被调用了多少次?如果这个函数被调用多次,那么你的事件将被调用多次。

 ibSerialPort.OnPacketReceived += ibSerialPort_OnPacketReceived;

作为测试,您可以在添加委托之前删除它:

ibSerialPort.OnPacketReceived -= ibSerialPort_OnPacketReceived;
ibSerialPort.OnPacketReceived += ibSerialPort_OnPacketReceived;

我想知道你定义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

这应该在你失去用户句柄之前完成(即在snull超出范围之前)。您可以考虑在订阅者中跟踪您的事件源,并使用Dispose方法为您取消订阅。

另外,正如其他人注意到的,你可以在订阅之前取消订阅:)我相信现在你已经有了你需要的解决方案。

我有同样的问题,在同步方法中注册您的事件(我把它放在form_loaded中)

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