При просмотре некоторого кода я столкнулся с простой ошибкой копирования / вставки, которая приводила к противоречивому интуитивному поведению. Намерение состояло в том, чтобы подписаться на события журнала событий системы и приложения и обработать их обоих. Свернутый код, представленный ниже, выглядит для меня так, как будто первый источник событий для журнала приложений заменяется и поэтому не должен генерировать больше событий после сборки мусора. Тем не менее, я наблюдаю, что любые записи в журнал приложения будут по-прежнему вызывать метод 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");
}
}