当事件被触发一次时,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!!!!");
}
试试这个,这将取消所有以前的订阅者:
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
超出范围之前)。您可以考虑在订阅者中跟踪您的事件源,并使用Dispose
方法为您取消订阅。
另外,正如其他人注意到的,你可以在订阅之前取消订阅:)我相信现在你已经有了你需要的解决方案。
我有同样的问题,在同步方法中注册您的事件(我把它放在form_loaded中)
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
RefreshHierarchy.COIDConflict += RefreshHierarchy_COIDConflict;
}