c# - 为什么事件处理程序在重新分配事件源后仍继续被调用?
问题描述
在查看一些代码时,我遇到了一个简单的复制/粘贴错误,导致反直觉行为。目的是订阅系统和应用程序事件日志事件并同时处理它们。在我看来,下面的精简代码好像应用程序日志的第一个事件生产者被替换,因此在垃圾收集后不应再产生任何事件。但是,我观察到对应用程序日志的任何写入仍将触发 ApplicationEventLog_EntryWritten 方法。
然后我假设某些东西保留了对原始 EventLog 实例的引用,但我想了解什么以及为什么?
class Program
{
public static EventLog ApplicationEventLog;
static void Main(string[] args)
{
ApplicationEventLog = new EventLog("Application");
ApplicationEventLog.EnableRaisingEvents = true;
ApplicationEventLog.EntryWritten += ApplicationEventLog_EntryWritten;
//new instance should have been assigned to new variable
//public static EventLog sytemEventLog;
ApplicationEventLog = new EventLog("System");
ApplicationEventLog.EnableRaisingEvents = true;
ApplicationEventLog.EntryWritten += SystemEventLog_EntryWritten;
//for illustrative purposes
//Original EventLog instance is no longer assigned to anything so I would expect it to get GC'd
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
Console.WriteLine("Press ESC to stop");
do
{
while (!Console.KeyAvailable)
{
Task.Delay(250);
}
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);
}
private static void ApplicationEventLog_EntryWritten(object sender, EntryWrittenEventArgs e)
{
Debug.WriteLine("Application log written to");
}
private static void SystemEventLog_EntryWritten(object sender, EntryWrittenEventArgs e)
{
Debug.WriteLine("System log written to");
}
}
更新示例:
此版本将所有事件日志代码从图片中删除,以获得更清晰的示例:
class Program
{
static void Main(string[] args)
{
var producer = new Producer();
producer.SomeEvent += EventConsumer1;
//Obviously unsubscribing before reassignment works but why does the subscription prevent GC of this first instance?
//producer.SomeEvent -= EventConsumer1;
producer = new Producer();
producer.SomeEvent += EventConsumer2;
Console.WriteLine("Press ESC to stop");
do
{
while (!Console.KeyAvailable)
{
//for illustrative purposes
//Original producer instance is no longer assigned to anything so I would expect it to get GC'd
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
Task.Delay(250);
}
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);
}
private static void EventConsumer1(object sender, EventArgs e)
{
Debug.WriteLine("I would have expected this to get hit once at most");
}
private static void EventConsumer2(object sender, EventArgs e)
{
Debug.WriteLine("Should hit this periodically");
}
}
public class Producer
{
public event EventHandler SomeEvent;
public Producer()
{
SendEventsAsync();
}
private async void SendEventsAsync()
{
while (true)
{
SomeEvent?.Invoke(this, new EventArgs());
await Task.Delay(5000);
}
}
~Producer()
{
Debug.WriteLine("Instance being garbage collected");
}
}
解决方案
推荐阅读
- reactjs - 我试图了解为什么 componentDidUpdate 没有针对特定组件触发
- tensorflow - 服务保存的模型时如何修复“Retval [0]已设置”
- javascript - 使用 CSS 或 jQuery 在 h 标签末尾添加行
- ruby - 如何在 Ruby on Rails 中使用 Selenium 单击链接?
- spring - 添加 JpaTransactionManager 配置时无法插入数据
- php - 如何运行这个 PHP 网站并将其托管在 HostGator 上?
- module - 在多个文件中使用模块时无法编译项目:“导入只能引用通过 --extern 传递的外部板条箱名称”
- ethereum - 安装 go-ethereum 时如何解决“意外的目录布局”错误?
- jenkins - Jenkins:仅在迁移文件夹已更改时运行迁移
- angularjs - Res.download() 传递数据而不是下载文件