C# 为什么即使在事件源被重新分配之后,事件处理程序仍继续被调用?

C# 为什么即使在事件源被重新分配之后,事件处理程序仍继续被调用?,c#,events,reference,event-handling,C#,Events,Reference,Event Handling,在查看一些代码时,我遇到了一个简单的复制/粘贴错误,导致了违反直觉的行为。其目的是订阅系统和应用程序事件日志事件并同时处理它们。在我看来,下面的简化代码似乎第一个事件生成器(用于应用程序日志)被替换,因此在垃圾收集之后不应再生成任何事件。然而,我观察到的是,对应用程序日志的任何写入仍将触发ApplicationEventLog\u EntryWrite方法 我假设有东西保留了对原始EventLog实例的引用,但我想了解是什么以及为什么 class Program { public sta

在查看一些代码时,我遇到了一个简单的复制/粘贴错误,导致了违反直觉的行为。其目的是订阅系统和应用程序事件日志事件并同时处理它们。在我看来,下面的简化代码似乎第一个事件生成器(用于应用程序日志)被替换,因此在垃圾收集之后不应再生成任何事件。然而,我观察到的是,对应用程序日志的任何写入仍将触发ApplicationEventLog\u EntryWrite方法

我假设有东西保留了对原始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");
    }
}

您仍然注册了事件处理程序,以清除使用
-=
事件延迟事件处理程序注销事件处理程序是常见的泄漏源。调用new()时,不会清除内部
EntryWrittenEventHandler
。处理以前的
ApplicationEventLog
(这也将调用
StopRaisingEvents
清理方法)。最好取消订阅您订阅的事件并处理一次性对象,对吗?@Christopher该链接似乎证实了我的困惑:那么“publisher”将使“target”保持活动状态,但“target”不会使“publisher”保持活动状态。
entrywriteneventhandler
委托不属于
ApplicationEventLog
对象。当您
new()
后者时,前者仍处于活动状态。新的
ApplicationEventLog
添加了一个新的处理程序。