But even with weak delegates, I've run into a case where an event handler is firing in an object that (as far as I can tell from the YourKit profiler) is not being referenced anywhere. I have reproduced the issue in a simple .NET 2.0 WinForms project in VS2005.
I have a form that contains 3 buttons. btnCreate creates an instance of a user control and adds it to the Controls collection of a panel. btnDelete calls a .Clear() method on the user control and removes it from the panel. btnChange changes the value of an application setting, defined under Properties / Settings.settings in the solution explorer. Here's the code in Form1:
Code: Select all
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnCreate_Click( object sender, EventArgs e )
{
if ( panel1.Controls.Count == 0 )
{
UserControl1 ctrl = new UserControl1();
panel1.Controls.Add(ctrl);
}
}
private void btnDelete_Click( object sender, EventArgs e )
{
UserControl1 ctrl = (UserControl1)panel1.Controls[0];
ctrl.Clear();
panel1.Controls.Clear();
}
private void btnChange_Click( object sender, EventArgs e )
{
Properties.Settings.Default.MySetting++;
}
}
Code: Select all
public partial class UserControl1 : UserControl
{
private List<string> _list = new List<string>();
public UserControl1()
{
InitializeComponent();
_list.Add("foo");
Properties.Settings.Default.PropertyChanged +=
(PropertyChangedEventHandler)Pvax.WeakDelegates.WeakDelegateDecorator.AddHandler(
Properties.Settings.Default, "PropertyChanged",
new PropertyChangedEventHandler(Settings_PropertyChanged));
}
public void Clear()
{
_list = null;
}
public void Settings_PropertyChanged( object sender, EventArgs e )
{
label1.Text = _list[0];
}
}
I have used the YourKit .NET Profiler to see what objects exist in memory after clicking each button in turn. After clicking btnCreate and taking a snapshot, the Profiler's object explorer tells me that I have an instance of Form1, an instance of Properties.Settings, and an instance of UserControl1. This is what I expect. After clicking btnDelete and taking another snapshot, the instance of UserControl1 is no longer listed in the object explorer. This is also what I expect, because nothing is referencing the user control any more.
So why does the event handler fire when I then click btnChange? The weak delegate should notice that the event sink no longer exists and silently unsubscribe from the event. I haven't had any problems with weak delegates in any other situation before.
I really can't understand what's going on in this case.