17

I have a blank Winform with a destructor method

public partial class Form1 : Form
{
    public Form1()
    {
        System.Diagnostics.Trace.WriteLine("Form1.Initialize " + this.GetHashCode().ToString());
        InitializeComponent();
    }
    ~Form1()
    {
        System.Diagnostics.Trace.WriteLine("Form1.Dispose " + this.GetHashCode().ToString());
    }
}

When the form is destroyed, I want it to write to the output window:

(Form1 opened)
Form1.Initialize 41149443
(Form1 closed)
Form1.Dispose 41149443

MSDN suggests 3 ways in implementing destructor:

However, none of these ways write "Form1.Dispose 41149443" to the output Window. Therefore, I am not able to tell whether the form has been destroyed or not. Suggestions ?

Should I give up hope on achieving this due to uncertainty of garbage collector?

Is there another way to know whether Form1 has been garbage collected ?

slfan
  • 8,950
  • 115
  • 65
  • 78
Jeson Martajaya
  • 6,996
  • 7
  • 54
  • 56

6 Answers6

12

Only one of the three ways to implement a destructor that you list actually involves a destructor, and that's ~Destructor().

If you implement IDisposable, and dispose of your object, then the code in Dispose will run, but there's no reason to think that your destructor will.

I think you chasing the impossible here. Destructors run as and when the garbage collector so decrees. It's not something that you have any control over. The GC is well within its rights to form the opinion that running destructors simply wastes time, and if there is plenty of memory it will form that opinion.

If you need predictable disposal, finalization etc., then use IDisposable.

Oded
  • 489,969
  • 99
  • 883
  • 1,009
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • IDisposable is not automatically predictable, you have to call Dispose or use using(). And how would you call Dispose()? – slfan Aug 03 '11 at 18:36
  • 2
    @slfan What are you talking about? `Dispose` runs when it is called, invariably via `using`. Where did I say that `Dispose` would run automatically? – David Heffernan Aug 03 '11 at 18:37
  • You wrote "if you need predictable disposal...". If you don't use using or call it explicitly, it's not called. As I have never used using with a form, it's even less predictable than the finalizer. – slfan Aug 03 '11 at 18:47
  • @slfan `IDisposable` exists to provide predictable disposal. What else is it for?! Should you so desire to implement it on a form, and use `using`, then the `Dispose` method would run at a predictable point. I didn't say it was a good idea to do that. What I did say was "If you implement `IDisposable`, **and dispose of your object**, then the code in `Dispose` will run". – David Heffernan Aug 03 '11 at 18:50
  • @slfan - I don't think he meant it that way. – JonH Aug 03 '11 at 18:50
  • I understand what you mean: 1) It is impossible to have a predictable destructor. 2) It is possible to have predictable IDisposable with using. More in http://blog.dmbcllc.com/2008/11/10/dispose-with-using/ – Jeson Martajaya Aug 03 '11 at 21:13
  • as @slfan says, IDisposable is not typically used on a form. – David Heffernan Aug 03 '11 at 21:25
  • 2
    Yes, and it does NOT call the finalizer either! That's why I would not call it a predictable disposal. It's just a standard pattern to work with unmanaged resources and any class that has a finalizer should implement the IDisposable pattern. But in my opinion this answer is not a reply to the question above. – slfan Aug 04 '11 at 05:35
  • 1
    @slfan I even say in the answer that the finalizer won't run. – David Heffernan Aug 04 '11 at 07:39
  • Hm… I am wonder, are *destructors* in C# have a mean at all? Usually it is used to free some resources, close file descriptors *(e.g. I am tried to close socket here)*, but since here no a hint about the time the destructor called, it is impossible to use it as usual. – Hi-Angel Nov 21 '14 at 06:40
  • For one, `Form`s already implement IDisposable. Secondly, if the `Form` is the main `Form` for the app, just wrap it in a `using` statement in `Program.cs`. – Thick_propheT Feb 17 '15 at 18:32
4

Unless you are studying garbage collection, a destructor is not the place for your tracing. You should look at Dispose (which is overridable in Form). This occurs after the unmanaged resource (like your window handle) has been released.

protected override void Dispose(bool disposing)
{
   System.Diagnostics.Trace.WriteLine(
      "Form1.Dispose " + (disposing ? "disposing " : "")
      + this.GetHashCode().ToString());
   base.Dispose (disposing);
}

If you want to see if a form/control has been disposed, use the Control.IsDisposed property.

Edit: Because of GC.SuppressFinalize, your Finalize method (destructor syntax in C#) will never execute if Dispose is called explicitly (or by the framework).

For more information, see Implementing a Dispose Method.

agent-j
  • 27,335
  • 5
  • 52
  • 79
  • @agent-j shouldn't that be `"Form1.Dispose " + (disposing ? "disposing " : "")` – apacay Aug 03 '11 at 18:35
  • @slfan, my point is that this is called by the finalizer only if necessary. If someone disposes the form explicitly, the destructor (finilize method) likely never is executed because of GC.SupressFinalize. See the Dispose pattern for more info. – agent-j Aug 03 '11 at 18:39
  • @agent-j: You're right, I haven't seen that Dispose() with the bool parameter is actually implemented in the Form class. Therefore your Dispose will be called from the finalizer or from the base method. But you should ALWAYS call base.Dispose(disposing); when you override this method. – slfan Aug 03 '11 at 18:44
2

Add a FormClosed event to the designer.

ie:

this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(Form1_FormClosed);

Then create the appropriate function to handle the event.

Raging Bull
  • 18,593
  • 13
  • 50
  • 55
1

Yes, you should probably give up on the idea of a destructor, because they're non-deterministic by nature. I'm not sure why you need to have the form disposed as opposed to just closed, but simply closing it should be enough in most cases.

You could use IDisposable but that depends on why you need the form to be garbage collected. If you need to re-use it, just create another instance.

kprobst
  • 16,165
  • 5
  • 32
  • 53
1

The Form gets garbage collected when no references exist and the garbage collector happens to run. You can force the garbage collector by calling GC.Collect(). You should not reference any other object within a Finalizer (aka destructor) because the object might have been garbage collected already.

You can use memory analyzer tools to find out whether your object is garbage collected or not, if you really need to.

You also have to keep in mind that the finalizer is called from a thread other than the main thread.

EDIT: If your problem is just that you don't see the trace output, you might have to turn autoflush on

<configuration>
  <system.diagnostics>
    <trace autoflush="true" />
  </system.diagnostics>
</configuration>

EDIT 2: There might be an external reference to your form, such as a registered event handler. I would suggest that you add a button in an administration area of your application which executes the following code:

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

Like this the garbage collector must run and should destroy your form (set a breakpoint into the finalizer). If not, you have some reference to the object which has to be destroyed.

slfan
  • 8,950
  • 115
  • 65
  • 78
0

You could try wrapping your form in a WeakReference object then check its IsAlive property to determine if it has been garbage collected. But, yes...as per the other answers you may be better off leaving it and trusting the GC to do its job!

IanR
  • 4,703
  • 3
  • 29
  • 27