Click here to Skip to main content
15,042,089 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I just want to see how destructor in c# is called, then I do the following test:
interestingly, the output is as follows

call constructotr...
call constructotr...
call constructotr...
call destructor...
call destructor...


you see that destructor is called only two times, does any one knows why?
thank you!

namespace DestructorTest
{
    class Class1
    {
        public Class1()
        {
            Console.WriteLine("call constructotr...");
        }
        ~Class1()
        {
            Console.WriteLine("call destructor...");
        }
    }
}


namespace DestructorTest
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i=0;i<3;i++)
            {
                Class1 c = new Class1();
            }
            GC.Collect(); 
            Console.ReadLine();
        }
    }
}


What I have tried:

see the "
Describe the problem
" for details.
Posted
Updated 10-Jan-21 9:45am
Comments
Richard MacCutchan 10-Jan-21 15:31pm
   
I get three of each.
Member 14707431 10-Jan-21 15:42pm
   
to be clear, you got three of each even without adding "c = null;" inside the loop?
Richard MacCutchan 11-Jan-21 4:37am
   
Yes, I ran your code exactly as it is.
Richard MacCutchan 11-Jan-21 4:41am
   
Having read OriginalGriff's solution and run further tests, I see why I got three calls with a very small program. Adding some extra code the third call to the destructor was delayed.

This is complicated, but simple, all at the same time.
The simple version?
The destructor is only called for unreferenced objects, and the third item you created is still referenced: the local variable c still contains it.

To prove that, kill the reference inside the loop:
C#
static void Main(string[] args)
    {
    for (int i = 0; i < 3; i++)
        {
        Class1 c = new Class1();
        c = null;
        }
    GC.Collect();
    Console.ReadLine();
    }
And you will get three calls to the destructor.

The complicated bit is that you think the variable c has been dereferenced - but it hasn't been, it's just gone out of scope.
That's not the same thing: the variable is allocated on the stack when the method is called, not when it is declared (so that regardless on the path through the method the number of stacked items to be removed is the same) so scope doesn't effect actual existence!

That's actually worth remembering: if you need a lot of variables (or some really big structs) in a single route through the method but not in others and stack space is at a premium the it's worth moving that path outside the method and into it's own ...
(But remember that an array of anything is a reference value, so it's on the heap anyway, except for the variable which references the array itself.)
   
Comments
Greg Utas 10-Jan-21 15:54pm
   
"Dereferenced"? And in Tony's post below, too. This is the first time I've seen it used to mean an object's reference count dropping to zero. Interesting.
OriginalGriff 10-Jan-21 16:50pm
   
What else would you call it? :laugh:
Luc Pattyn 10-Jan-21 16:49pm
   
Excellent answer, the only one mentioning stack.

How about a compiler option to automatically nullify all references that go out of scope?

:)
OriginalGriff 10-Jan-21 16:52pm
   
Unlikely, but useful. It would be an anti-performance option, and that kinda runs contrary to most compiler designers whole raison d'être! :D
Have you tried forcing the object to not be referenced like this

for (int i=0;i<3;i++)
{
    Class1 c = new Class1();

    c = null;
}

GC.Collect();

Console.ReadLine();


The destructor for the last object does get called when the program exits but as your console window has closed you don't see the final Console.WriteLine();

Because the last object was not specifically dereferenced the GC did not touch it, the previous 2 objects were dereferenced by the repeated use of the same variable name.

Hope that makes sense.
   
Comments
Member 14707431 10-Jan-21 16:00pm
   
thank you! but I am not so sure about what you said by "The destructor for the last object does get called when the program exits but as your console window has closed you don't see the final Console.WriteLine();"
Believe you know you cannot call the destructor in .NET. CLR handles the managed heap.

Now, you can however define a destructor to a class and this destructor would be called once the object gets collected by the GC. Details: GC Class (System) | Microsoft Docs[^]

Quote:
Periodically, the garbage collector performs garbage collection to reclaim memory allocated to objects for which there are no valid references.


With your example, I would seems GC still finding the reference of the last managed object and thus you see one less than anticipated. Close to what you are trying and making sure cleanup can happen, try:
C#
using System;
					
public class Program
{
    public static void Main(string[] args)
    {
		for (int i=0;i<3;i++)
		{
			//Class1 c = new Class1();
			Init();
		}
		
		GC.Collect(); 
        Console.ReadLine();
    }
	
	public static void Init()
	{
		Class1 c = new Class1();
	}
}

public class Class1
{
	public Class1()
	{
		Console.WriteLine("call constructotr...");
	}
	~Class1()
	{
		Console.WriteLine("call destructor...");
	}
}

/*
Output
call constructotr...
call constructotr...
call constructotr...
call destructor...
call destructor...
call destructor...
*/

Generally, when you have unmanaged resources in play, you make use of IDisposable and then use GC for suppress and collect. So, if you want to mix GC with Desctructor calls, you need to implement it correctly[^].
   
Comments
Member 14707431 10-Jan-21 15:51pm
   
Thank you for your comments. Actually, I have also tried this way. you are right, if I put the loop in a separate method, then once outside of the method, then all the instances are not referenced and then we get 3 destructors. I have assumed outside the loop the 3 instances should have been also unreferenced, actually seems not.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900