当事件被引发一次时,多次调用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!!!!"); } 

试试这个,这将取消注册任何普通用户:

 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 

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

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

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

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