I have a CF application that UserControls flows over time. This took some time, but I narrowed it down and even reproduced the behavior in the full structure (3.5). Since the behavior exists in both cases, I don’t want to call it a mistake, but I’m sure I don’t understand why this is happening, and I hope that someone can shed light on it.
Therefore, I am creating a simple WinForms application with a form and a button. Clicking the button alternates between creating a new UserControl and Disposing this control. Very simple.
public partial class Form1 : Form { public Form1() { InitializeComponent(); } UserControl1 m_ctl; private void button1_Click(object sender, EventArgs e) { if (m_ctl == null) { m_ctl = new UserControl1(); m_ctl.Visible = true; this.Controls.Add(m_ctl); } else { this.Controls.Remove(m_ctl); m_ctl.Dispose(); m_ctl = null; GC.Collect(); } } }
And here is the UserControl. It simply keeps track of the number of live (i.e., incomplete) instances. He has nothing but a single mark so that I can visually confirm it in the form.
public partial class UserControl1 : UserControl { private static int m_instanceCount = 0; public UserControl1() { var c = Interlocked.Increment(ref m_instanceCount); Debug.WriteLine("Instances: " + c.ToString()); InitializeComponent(); } ~UserControl1() { var c = Interlocked.Decrement(ref m_instanceCount); Debug.WriteLine("Instances: " + c.ToString()); } }
The strange thing here is that the number of instances is growing endlessly. In the end, my device runs out of memory. I suspect I'm on a PC too, I'm just not inclined to press a button for next year.
Now, if I change the default UserControl method, the constructor-created Dispose method like this is just adding a call to ReRegisterForFinalize:
protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); if (disposing) { GC.ReRegisterForFinalize(this); } }
Then it behaves exactly as expected, completing the instances at the time of collection (with manual or automatic).
So why is this happening? Obviously, the base calls SuppressFinalize, but why exactly is this happening, and why is this default behavior in Odin's name?