0
Imports System
Module module1
    Class suman
        Private c As Integer
        Sub New()
            Console.WriteLine("Default constructor")
        End Sub

        Sub New(ByVal x As Integer, ByVal y As Integer)
            c = x + y
            Console.WriteLine(c)
        End Sub
        Sub New(ByVal obb As suman)
            c = obb.c
            Console.WriteLine(obb.c)
        End Sub
        Protected Overrides Sub finalize()
            Console.WriteLine("byebye")
        End Sub
    End Class

    Sub main()
        Dim obj1 As suman = New suman()
        Dim obj2 As suman = New suman(10, 20)
        Dim obj3 As suman = New suman(obj2)

    End Sub
End Module
Steve
  • 213,761
  • 22
  • 232
  • 286
  • Destructor is "showing" for me. You will possibly not see it if you debug your code and do not start it from a console window, Destructor will fire with `End Sub`. – Storax Jul 15 '22 at 18:04
  • 1
    Move the three lines of code in a separate sub, call this sub from main, add GC.Collect() after the call, finally add a Console.ReadLine and you will see the destructor even in a debug session – Steve Jul 15 '22 at 18:12
  • 1
    A finalizer is not guaranteed to run, ever---it's best not to think of it as a "destructor". If you care about something running when an object goes away, the correct way to do so is to implement `IDisposable` and make sure that anything that uses it does so under `Using` or equivalent code. `Dispose` is much more akin to a destructor than is a finalizer. – Craig Jul 18 '22 at 15:00
  • @Craig If Finalize is overridden and has an implementation, the GC will run the finalizer as part of object destruction; if you define it, the GC will run it. If you do not override and define Finalize, the GC will ignore it. See the GC + Finalize documentation for more details. As long as the program doesn't crash the CLR with a native fault or some other hard crash (loss of power, hard shutdown/reboot), it will run the finalizer, as I said given it is overridden and defined. There are definitely no guarantees, but there are certain assurances afforded by the CLR. – jhyry-gcpud Jul 18 '22 at 16:25
  • 1
    @jhyry-gcpud Also see Eric Lippert's article on the point: https://ericlippert.com/2015/05/18/when-everything-you-know-is-wrong-part-one/ which is why I say it is not guaranteed to run. If I remember correctly, Eric's advice on finalizers is never to have anything non-trivial (beyond calling `Dispose`) in a finalizer. – Craig Jul 18 '22 at 16:55
  • 1
    @Craig That is a fantastic series that everyone should read. I have read it before. I guess I'm speaking from a perspective having experience with and taking significant time to learn about the CLR GC process. I think I'm traveling outside the purview of OP with what I'm saying. When you're writing code that's not a toy, (intended for tests, tooling, production systems, etc.) it is wise to be extremely mindful of what you're putting in a finalizer (and what you're putting in `Dispose` for that matter, can it be cleaned up earlier? if so then it should be). Clean-up should be ASAP. – jhyry-gcpud Jul 18 '22 at 17:13

1 Answers1

3

You should take a look at this documentation: How Finalization works

The output in the console window should be visible as long as you're running it in a terminal/command-prompt window that does not immediately close. As Steve said in his comment, you can add a Console.ReadLine() to stop a debug session from terminating, but this does not guarantee you will see the output. The CLR Garbage Collector determines what and when to collect, and this may not happen prior to your Console.ReadLine() (it probably will with a call to GC.Collect() but the GC is not obligated to act on said call in all cases). It is very important to note also the finalization and collection are two completely separate processes (in a workflow sense, not in an operating system sense) and concepts in the CLR GC, so just because your object got collected does not mean Finalize has been run or will be run at all (it will probably run but is not guaranteed to run; the CLR makes a "good-faith effort" to make sure everything is run but this is not guaranteed in all cases).

To accomplish what you're asking with the program still running, it is necessary to do it in a separate function so the local objects you created get destroyed when the function exits (credit for this also goes to Steve, I believe he is correct).

You can make a small Sub with the code you currently have in your Main and call it from Main, then call GC.Collect() and Console.ReadLine() after that Sub successfully exits:

    Sub Main()
        CreateAndDestroyObjects()
        GC.Collect()
        Console.ReadLine()
    End Sub

    Sub CreateAndDestroyObjects()
        Dim obj1 As suman = New suman()
        Dim obj2 As suman = New suman(10, 20)
        Dim obj3 As suman = New suman(obj2)
    End Sub

See documentation on GC.Collect() for more details: GC.Collect in System

In short, this is a matter of timing. In a debug session, you can also set the window to stay open if you're using a recent version of Visual Studio: Preventing console window from closing If you're in an older version of Visual Studio and you're trying to see this in debug, a Console.ReadLine() is probably your best bet.

UPDATE:

Craig's comment should be heeded as very good advice. Handling it through IDisposable is a much more "correct" and "proper" way to accomplish this to ensure clean-up code gets run when an object is destroyed; my original answer was more specific to the very limited aim of OP, but ensuring code is run when an object is destroyed in production code requires more: implementation of IDisposable, Using blocks, Try-Catch-Finally (for objects that do not implement IDisposable), and addition of clean-up code in Finalize for native resources that do not get handled by the garbage collector (deallocation of native objects, closing files opened by native code, etc.).

Another point to be taken from Craig's comments: a finalizer is not a destructor in the native C++ sense. It's a managed construct used by the garbage collector to do certain tasks related to reclaiming memory and destroying objects that appear to no longer be reachable/in-use. Great care should be taken when actually writing a real finalizer intended to be used in an application or service; Dispose should be used exclusively in almost all cases.

I know this answer has gone far beyond answering OP, though this is a really important subject in almost all real-world programming (.Net or otherwise) and bears repeating when the subject comes up.

ANOTHER UPDATE:

Here's a chunk of good information that I was painfully unaware of: SafeHandle

SafeHandle should in theory make Finalize completely unnecessary outside of calling Dispose.

jhyry-gcpud
  • 155
  • 2
  • 8