【问题标题】:c# event handler is called multiple times when event is raised oncec#事件处理程序在一次引发事件时被多次调用
【发布时间】:2018-03-27 20:57:14
【问题描述】:

下面是我的代码,第一部分是我引发事件的地方,第二部分是我在另一个类中使用它的地方。看起来很简单,但日志显示即使事件被引发一次,事件在使用该事件的类上触发了 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
  • 请记住,即使您认为您的订阅者已被释放,也会保留订阅的参考。

标签: c# events serial-port raiseevent


【解决方案1】:

试试这个,这将取消注册任何上一个订阅者:

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

【讨论】:

  • 是的,如果这是方法代码,那么它会在每次调用时重新注册事件。
  • ibSerialPort 是一个实例声明。一个 COM 对象正在使用这个类,我想知道 ibSerialPort.OnPacketReceived += ibSerialPort_OnPacketReceived;被多次调用。感谢您的快速回复!
  • 好答案,和我的一样。
  • 我没有抄袭你我向你保证,这是一种很常见的技术
  • 不指责偷窃,只是说......真棒答案!
【解决方案2】:

这被调用了多少次?如果这被多次调用,那么您的事件将被多次调用。

 ibSerialPort.OnPacketReceived += ibSerialPort_OnPacketReceived;

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

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

【讨论】:

  • 这对我来说简直就是一种魅力......没有注意到我一直在向控件添加越来越多的事件
【解决方案3】:

我想知道您定义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 方法为您取消订阅。

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

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-25
      • 2011-07-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多