Amazon.com Widgets

Even more fun with virtual methods

OK, after the last post I expect many people will nail this, so why don’t you show it your friends that don’t read my blog and see if they pass ;-) 

Create a new instance of Derived, drop the reference, wait for the GC to collect it, and for any finalizers to run.  What is the output? 

And of course, the important question: why?

 

public class Base

{

    public virtual void DoCleanUp() {

        Console.WriteLine("Do Base's Cleanup");

    }

    ~Base()

    {

        DoCleanUp();

    }

}

public class Derived: Base

{

    public override void DoCleanUp()

    {

        Console.WriteLine("Do Derived Cleanup");

    }

    ~Derived()

    {

        DoCleanUp();

    }

}

 

Published 11 August 04 07:57 by BradA

Comments

# Buddhike de Silva said on August 11, 2004 8:24 PM:
The result is "Do Derived Cleanup" prints twice on the screen.
Reson:

Destructors are called in the opssite way to the contructors are called. So ~Derive is called and it prints one "Do Derived Cleanup". Since it is inherited from Base then the ~Base is called. But the Call do DoCleanup from Base causes to call the DoCleanup method in the derive class since the object instance is Derive. Consequently it prints "Do Derived Cleanup" again!
# Stéphane Lajoie said on August 11, 2004 8:32 PM:
My guess: it will print "Do Derived Cleanup" twice.

The reason I believe is that a Finalize method (~Derived) automatically calls the base class' Finalize (~Base) before returning, but the object maintains its type (Derived) all along so that both calls to DoCleanUp end up in Derived.DoCleanUp.

So this really is the same thing as the previous quiz, with the addition of the automatic base.Finalize() call.

I haven't tested any of this though.

And I have no friends to show it to, they're all a bunch of Linux freaks :).
# Stuart said on August 11, 2004 8:43 PM:
I think the answer is this:

Do Derived Cleanup
Do Derived Cleanup

Figuring out why is left as an exercise for another reader. Assuming I'm right, of course.
# Omer van Kloeten said on August 11, 2004 8:59 PM:
The output is:

Do Derived Cleanup
Do Derived Cleanup

This is because the Finalizer on Derived is called first, invoking its DoCleanUp method, then the Finalizer on Base is called, invoking DoCleanUp, which by being virtual, activates the derived class's overriding method.
# Dan McKinley said on August 11, 2004 9:06 PM:
Piece of cake - "Do Derived Cleanup" is printed twice, because all of the finalizers are called.
# Ken said on August 11, 2004 10:18 PM:
Derived's DoCleanUp will be called twice, once by Derived's "destructor", and once by Base's.
# Martin Hueser said on August 11, 2004 11:36 PM:
Derived.DoCleanup will be called twice. First from inside ~Derived(), second from inside ~Base().

~Derived() body is executed first, which calls Derived's DoCleanUp(). After that all base class destructors are called. The DoCleanUp() call in ~Base() calls Derived's DoCleanup() because, it's a Derived object which is currently destructing and that has overridden Base.DoCleanUp()
# Sean Malloy said on August 11, 2004 11:55 PM:
Destructors are implicitly chained.

so Derived.DoCleanUp gets called twice, because the call in Base object calls the Derived override.

Looking at the docos: "destructors in an inheritance chain are called in order, from most derived to least derived."
# Kristof Verbiest said on August 12, 2004 12:03 AM:
First, the Derived destructor is invoked which will print "Do Derived Cleanup". Then, the base destructor will be invoked which will also print "Do Derived Cleanup" because the method is virtual.
# Hamza GOLYERI said on August 12, 2004 12:57 AM:
The output is:

Do Derived Cleanup
Do Derived Cleanup

When Derived's Finalize method calls Base's Finalize method (after it completes it's own cleanup) Base's Finalize method makes a virtual call to DoClean, which is dispatched to overridden implementation of class Derived.

So Base's cleanup is not done and resource leakage might occur.

HG
# Alex said on August 12, 2004 4:09 AM:
OK, let me try.
Output should be twice:
"Do Derived Cleanup"
"Do Derived Cleanup"
This is due the finalizer of the Derived class will call the finalizer of the Base and Base will therefore call the overridden DoCleanUp() on the Derived again, since we have an instance of Derived and not Base.

Base calls this.DoCleanUp();. Then Base calls this.Finalize(); In fact we have two finalizations. Is this necessary? Do we have to supress the second finalize?

Alex
# David Levine said on August 12, 2004 5:24 AM:
My first impulse is that you will see...

Do Derived Cleanup
Do Derived Cleanup

I ran this as a test and sure enough that's what it printed out.

The reason is that objects are created from the inside-out and finalized from the outside-in, and unless eplicitly overridden, invoking a virtual method will call into the virtual method in a derived class, not from the base class. So the order of execution is...

1. Invoke ~Derived(), which invokes
2. Derived.DoCleanUp()
3. invoke ~Base(), which invokes
4. Derived.DoCleanUp()



# Brandon Furtwangler said on August 12, 2004 6:34 AM:
I would guess you would get:

Do Derived Cleanup
Do Derived Cleanup

because it will call both destructors and each will called Derived's override of DoCleanUp. Sadly I've never even used descructors in C# so I could be way off on this one.
# Mark Hiscocks said on August 12, 2004 6:36 AM:
The output will be:
"Do Derived Cleanup"
"Do Base's Cleanup"

The first destructor to get called will be that of Derived. Then, by the time Base's destructor gets called the Derived object no longer exists, nor does it's vtable, therefore Base::DoCleanUp is called instead.
# Mark Mullin said on August 12, 2004 6:46 AM:
That's easy

what
Do Derived Cleanup
Do Derived Cleanup

why

When the derived class finalizer runs it calls the overloaded DoCleanUp. Then the base class finalizer gets invoked - invoking the base class finalizer on an instance of a derived class means the base class is operating on an instance of the derived class - so it's DoCleanUp ends up invoking the derived classes DoCleanUp again

so I have a question

Is there any way to have a base class indicate it wants to call it's own version of a virtual and not one of the overrides when the instance is a derivative ?
# Tobin Titus said on August 12, 2004 7:12 AM:
The console will display the derived cleanup's code twice.
# Geoff Appleby said on August 12, 2004 7:16 AM:
I'm fairly sure that _both_ lines are going to be printed to the console.
What i'm not sure of is the order - my feeling is that the object is destroyed, and then the base object is destroyed, so it would print in that order. But i don't know. :)
# sebmol said on August 12, 2004 7:17 AM:
Impossible to tell. There is no guarantee that finalizers are ever executed. If they were, you should see "Do Derived Cleanup" twice for the same reasons you'd see it twice if it was invoked in the constructor.
# J.Marsch said on August 12, 2004 7:33 AM:
Ok, my first time trying to really analyze IL, so go easy on me:

In the IL for Derived.Finalize, we perform a callvirt instance void Base::DoCleanup() Since it's a callvirt, we get the derived version of DoCleanup().

Next, we perform a call instance void Base::Finalize()
That is going to invoke Base's finalizer.
Base.Finalize also peforms a callvirt to Base::DoCleanup(). Since it's a callvirt, we get the overridden implementation of DoCleanup().

Is that close?
# Philip Rieck said on August 12, 2004 7:47 AM:
Since no-one else has commented, I will. If I'm wrong (since I don't have a compiler near me) Then this is the *other* Philip Rieck.

This would print out:
Do Derived Cleanup
Do Derived Cleanup

Why? Well, two reasons, really -- Firstly, you really, really should call your base classes Finalize method in your Finalize. Since this is such a strong rule, the C# compiler forces you to. That is, at the end of your Finalize method a call to Base.Finalize() is added at compile time.

I'm not sure, but I do think that if you try to add this call yourself you get a compile error.

Second reason, In Base.Finalizer you will have a "callvirt instance void Base::DoCleanup()" -- and since this object is a "Derived" object, it will call the most derived DoCleanUp accessible -- Derived.DoCleanUp().

This is so similar to the dispose pattern that a code reviewer should really say "Make this the Dispose convention used in .net, ensure it gets called once (Disposed = true), and call Base.Dispose from derived (in a finally block).
# Merak said on August 12, 2004 8:10 AM:
The derived DoCleanUp gets called twice.
First when Derived is finalized, then again when Base is finalized (due to the DoCleanUp call being overriden)

It would probably have been better written if the base DoCleanUp, and the finalizer both called a common (private) cleanup function.

(or relying on Derived to call base.DoCleanUp <shudder>)
# Merak said on August 12, 2004 8:11 AM:
The derived DoCleanUp gets called twice.
First when Derived is finalized, then again when Base is finalized (due to the DoCleanUp call being overriden)

It would probably have been better written if the base DoCleanUp, and the finalizer both called a common (private) cleanup function.

(or relying on Derived to call base.DoCleanUp <shudder>)
# YM said on August 12, 2004 8:12 AM:
IMHO the following might illustrate the point a bit better

public class Base

{

public virtual void DoCleanUp() {

Console.WriteLine("Do Base's Cleanup");

}

~Base()

{

Console.WriteLine("~Base() started");
DoCleanUp();
Console.WriteLine("~Base() completed");


}

}

public class Derived: Base

{


public override void DoCleanUp()

{

Console.WriteLine("Do Derived Cleanup");

}

~Derived()

{

Console.WriteLine("~Derived() started");
DoCleanUp();
Console.WriteLine("~Derived() completed");

}

}
# YM said on August 12, 2004 8:13 AM:
IMHO the following might illustrate the point a bit better

public class Base

{

public virtual void DoCleanUp() {

Console.WriteLine("Do Base's Cleanup");

}

~Base()

{

Console.WriteLine("~Base() started");
DoCleanUp();
Console.WriteLine("~Base() completed");


}

}

public class Derived: Base

{


public override void DoCleanUp()

{

Console.WriteLine("Do Derived Cleanup");

}

~Derived()

{

Console.WriteLine("~Derived() started");
DoCleanUp();
Console.WriteLine("~Derived() completed");

}

}
# Merak said on August 12, 2004 8:18 AM:
Actually (adding to my previous reply) I'm really surprised that Console is even available during finalization !
# David A. Mellis said on August 12, 2004 8:58 AM:
It will output "Do Derived Cleanup" twice.

Once when the derived class's destructor executes, and again when the automatically-generated call to the base class's destructor executes. In both cases, the object is still an instance of Derived, so normal virtual method resolution applies.
# Max said on August 12, 2004 9:01 AM:
It prints
"Do Derived Cleanup" two times.

The reason: The Dtor of Derived calls the virtual method DoCleanUp wich prints "Do Derived Cleanup" the first time. Next the Dtor of Base gets automatically called after the Derived Dtor is finished. The Dtor of Base again calls DoCleanUp and since DoCleanUp ist virtual Derived.DoCleanUp gets called a second time.
# Josh Pollard said on August 12, 2004 9:04 AM:
It will call the Derived class' DoCleanUp method twice. I don't know the exact reason. I do know that it is because it calls it once to kill the derived class object, and once again when it attempts to kill the base object. It does this because of it being a virtual method that is overridden.
# RJ said on August 12, 2004 9:33 AM:
I'm going to be so embarrised if I get this wrong, but....

I think it will print "Do Derived Cleanup" twice.

Reason: destructors will be called outer to inner. But the inner constructor will callvirt the outer's DoCleanUp method.

Oh lord, please say it ain't so.
# Dilip said on August 12, 2004 9:35 AM:
"Do Derived Cleanup" is printed 2 times. Probably because by the time the finalizer for the base portion is executed the object is still not fully collected which preserves the virtual dispatch mechanism. So the DoCleanup call in ~Base() still translates to a callvirt instruction thereby calling Derived.DoCleanUp() -- that is if you obviously have created a Derived object in main().
# Stuart said on August 12, 2004 11:20 AM:
Moderated comments suck...

Looking forward to hopefully seeing the comments posted to this one before the article falls off the bottom of blogs.msdn.com...
# Stuart said on August 12, 2004 11:22 AM:
Hey, that actually went through? Okay, well in that case, I'll post my answer (which I also posted last night, but it went into the moderation void)...

I believe the result will be:

Do Derived Cleanup
Do Derived Cleanup

I haven't tested this but I'm pretty confident in the hypothesis. I'll leave it to others to explain why - assuming I'm right, of course.
# Diego Mijelshon said on August 12, 2004 11:42 AM:
As Stuart mentioned, it's:
Do Derived Cleanup
Do Derived Cleanup

The first one corresponds to Derived and the second to Base, since destructors are called in reverse order.

This could be a big problem if the classes where dealing with real resources.
# Random coder said on August 12, 2004 12:30 PM:
Is the solution to avoid calling virtual methods in destructors, having a policy that says methods like DoCleanUp() must call base.DoCleanUp() as the last line, or something else? This is a little off the top of my head, so these suggestions might be simplistic.
# Mark H said on August 12, 2004 12:46 PM:
The output will be:

Do Derived Cleanup
Do Base's Cleanup

The Derived object's destructor will get called and all reference to the Derived instance will be removed. What is left is a Base object. The Base object's destructor will then get called, but since the Derived aspect of the object no longer exists, the Base::DoCleanUp is call instead.
# Robert Kozak said on August 12, 2004 1:29 PM:
Looks like some people are confused on this issue.

I agree with Mark H. As soon as I saw it I knew that the output would be:

Do Derived Cleanup
Do Base's Cleanup

And I think this makes perfect sense.
# Darren Bennett said on August 12, 2004 1:49 PM:
Output:
Do Derived Cleanup
Do Derived Cleanup

The first corresponds to the ~Base destructor (since base objects are GC'd first). Since the destructor is calling a virtual method it calls the version of DoCleanup from the Derived class

The second corresponds to the ~Derived destructor (since it is the last object in the v-table set for destruction). It calls the same DoCleanup, thus getting duplicate output.
# Darren Bennett said on August 12, 2004 1:49 PM:
Output:
Do Derived Cleanup
Do Derived Cleanup

The first corresponds to the ~Base destructor (since base objects are GC'd first). Since the destructor is calling a virtual method it calls the version of DoCleanup from the Derived class

The second corresponds to the ~Derived destructor (since it is the last object in the v-table set for destruction). It calls the same DoCleanup, thus getting duplicate output.
# IanG on Tap said on August 12, 2004 5:17 PM:
# JD said on August 12, 2004 3:45 PM:
Robert Kozak, Mike H: You're expecting the C++ behavior.

The CLR is avoiding that fixup work done by C++.
# Robert Kozak said on August 12, 2004 8:21 PM:
Did you try it JD?

:-)
# Ian Griffiths said on August 13, 2004 12:40 AM:
I don't know if JD did, but *I* did. JD is right, and you are wrong Robert. Did *you* try it? ;-)
# Mark H said on August 13, 2004 3:55 AM:
Ah, you guys cheated and actually compiled and ran the code! Since when are quizes open book? ;)
# Robert Kozak said on August 13, 2004 8:32 AM:
One of my co workers compiled it after I showed it to him. I initially thought it would be
Do Derived Cleanup
Do Derived Cleanup

But when he ran the code it was

Do Derived Cleanup
Do Base's Cleanup

Now I have to go back and see what is going on. Now I am thoroughly confused :-)
# Robert Kozak said on August 13, 2004 9:17 AM:
Damn. Ok double checked the code. My buddy cut and pasted and forgot to change the virtual to an override. I feel like an idiot. But no worries thats a normal feeling for me. :-)
# Brad Abrams Even more fun with virtual methods | patio umbrella said on June 17, 2009 11:41 PM:

PingBack from http://patioumbrellasource.info/story.php?id=231

New Comments to this post are disabled

Search

Go

This Blog

Syndication

Page view tracker