Amazon.com Widgets

Quiz: Virtual Methods

Under what circumstance will the following code print: “value != 42, what is wrong?”

 

I tested this quiz out on Lutz, his first question was “does it use reflection”… typical from the reflector guy ;-)

Anyway, nope, no “funny business”… It is 100% verifiable code that would pass just about any code review.  

 

 

public class Derived: Base

{

    private int value;

    public Derived()

    {

        value = 42;

    }

 

    public override void Method1()

    {

        if (value == 42) Console.WriteLine("value == 42, all is good");

        else Console.WriteLine("value != 42, what is wrong?");

    }

}

Published 09 August 04 09:53 by BradA

Comments

# Larry Osterman said on August 9, 2004 10:02 PM:
Off the top of my head:

What happens if a class DerivedAgain is declared that derives from Derived and replaces the value=42 in the constructor with something other than 42?

Similarly, does C# guarantee that constructors are called in hierarchical order? If not, then base could declare a value, and change the 42 to something else.

Another possibility has to do with the use of the name "value" - isn't "value" a reserved word? Or is that only in the context of property accessors?



# Ken said on August 9, 2004 10:09 PM:
Since C# allows virtual dispatch on an object that hasn't finished all its constructors, value won't be set to 42 (yet) if Base's constructor calls Method1, but it will execute Derived's override, causing the "what is wrong?" message to be shown.
# Geoff Appleby said on August 9, 2004 10:31 PM:
my first thought was:

public void MakeItWrong()
{
Base BaseObject = new Base();
Derived DerivedObject = (Derived)BaseObject;
DerivedObject.Method1();
}

but that feels utterly wrong :)

On thinking properly, I'll agree with Larry.
# JFo said on August 9, 2004 10:46 PM:
When the base class looks like so:

public class Base {
public Base() {
Method1();
}
public virtual void Method1() {
}
}

value will be 0 in the overrided method as the Derived constructor hasnt executed yet.

Is it cheating if you know the FXCop rule? =)
http://dotnetjunkies.com/WebLog/mlevison/archive/2004/04/22/11976.aspx
# Wesner Moise said on August 9, 2004 10:57 PM:
The answer is as follows:

In C#, a constructor call works as follows:

public Derived()
{
// derived class initializers are initialized in the order they are declared
// base constructors are called (and base initializers are initialized)
// body of derived constructor is called
}


So, in the following example,

public Derived : base
{
private value = 21;
public Derived()
{
value = 42;
}

public override Method1()
{
}
}


public Base
{
public Base()
{
Method1();
}

public virtual Method1()
{
}
}

In this case, when Method1 is called by the Base constructor, the value of value is 21, not 42! Note, the value is not 0, because the initializers execute before the base constructors are ever called.

# Matthew W. Jackson said on August 9, 2004 11:42 PM:
Yep. If the base class calls a virtual method then weird things like this can happen.

On a side note, don't a LOT of Windows Forms classes set virtual properties in their constructors?

There are a few ways to get around the problem if you absolutely NEED to call a virtual method during construction, but it seems like it would be nice if the framework could somehow optionally call a method on a base class after all constructors have executed.

Maybe via an interface such as IInitializable (I don't think that's a word) would work, but there could be problems if multiple levels in the inheritance chain needed to be notified.

Perhaps the runtime could let you hook up an event in your constructor that would fire exactly once for any object and then automatically unhook all listening events...It would probably work best as a protected event on the Object class, and could be used like this:

class Base {
protected Base() {
this.ConstructionComplete += this.Initialize();
}

private void Initialize() {
this.SomeVirtualMethod();
}

protected virtual void SomeVirtualMethod() {
}

}

I think other people have suggested this kind of functionality before. Is this feasable at all? How deep into the runtime would a feature like this actually dig? Would there be any performance problems?
# Haacked said on August 10, 2004 12:26 AM:
This is probably not the answer you were looking for, but how about if you call it via C++?

Derived* derived = new Derived();
derived->Base::Method1();

Assuming Base.Method1() looks like

public virtual Method1()
{
Console.WriteLine("value!=42, what is wrong?");
}

;) yes. cheesy.
# sbjorg said on August 10, 2004 1:44 AM:
That's one of my standard interview questions for SDEs. if a programmer doesn't know the semantics of the programming language they use, he/she shouldn't be programming. Sadly, I've come across way too many individuals who didn't know. I'm glad to say they didn't get hired.

Of course, the real culprit is the constructor construct. BTW, in C++ the semantics are different. Hopefully some day we'll realize our mistake and remove constructors from future languages.
# Cyrus Najmabadi said on August 10, 2004 4:49 AM:
Sbjorg. That's pretty silly IMO. Languages are incredibly complex beasts and it's quite possible for someone to not know every little intricacy in it. A true developer would be someone who would have no problem picking up and understanding those intracacies when it came to it.
# Jamie Cansdale said on August 10, 2004 5:44 AM:
Okay so it would fail just about any code review and definately counts as “funny business”. It's here just for completeness... ;)

Derived derived = (Derived)FormatterServices.GetUninitializedObject(typeof(Derived));
derived.Method1();
# Stuart said on August 10, 2004 6:14 AM:
I figured out the answer to the quiz as written, but I ran a quick test and I can't figure out why you *don't* see this behavior if you remove the Derived constructor and just put "private int value = 42". I thought that field initializer expressions just got inserted into the constructor by the compiler?
# Jason Whittington said on August 10, 2004 7:08 AM:
Stuart,

The reason you don't see this behavior if you put it into the field initializer is because of how the constructor is generated. The order in C# is:

Field initializer for derived
call base ctor
call derived ctor

Interestingly this is NOT the order in VB, where all of the base stuff (initializers + ctor bodies) run before all of the derived class stuff. Compare the output of the folloing two "identical" programs in VB and C#. The C# program outputs:

baz running
bar running
foo running
A ctor
B ctor
C ctor

The VB code outputs

foo running
A ctor
bar running
B ctor
baz running
C ctor

I'll leave it as an exercise to the student to consider the cross-language inheritance implications.

I enjoy your quizzes, Brad - keep them up!

C# code
----------------------------------------------------------------
using System;

class Util
{
public static int foo()
{
Console.WriteLine("foo running");
return 1;
}

public static int bar()
{
Console.WriteLine("bar running");
return 2;
}

public static int baz()
{
Console.WriteLine("baz running");
return 3;
}
}

class A
{
int a = Util.foo();
public A() { Console.WriteLine("A ctor"); }
}

class B : A
{
int b = Util.bar();
public B() { Console.WriteLine("B ctor"); }
}

class C : B
{
int c = Util.baz();
public C() { Console.WriteLine("C ctor"); }
}


class App
{
static void Main()
{
new C();
}

}


The "same" code in VB
----------------------------------------------------------
Imports System

Class Util
Shared function foo as Integer
Console.WriteLine("foo running")
return 1
End Function

Shared function bar as Integer
Console.WriteLine("bar running")
return 2
end Function

Shared function baz as Integer
Console.WriteLine("baz running")
return 3
end Function

End Class


Class A
Dim A as Integer = Util.foo()

public sub New()
Console.WriteLine("A ctor")
end Sub
end class


class B : Inherits A
Dim B as Integer = Util.bar()

public sub New()
Console.WriteLine("B ctor")
end Sub
end class

class C : Inherits B
Dim A as Integer = Util.baz()

public sub New()
Console.WriteLine("C ctor")
end Sub
end class



class App

Shared Sub Main()
Dim c As C = new C
End Sub

End Class

# AndrewSeven said on August 10, 2004 9:06 AM:
IIRC FxCop warns you about using a virtual call in the ctor
# Fabrice's weblog said on August 11, 2004 12:05 PM:
# Fabrice's weblog said on August 11, 2004 12:06 PM:
# Fabrice's weblog said on August 11, 2004 12:23 PM:
New Comments to this post are disabled

Search

Go

This Blog

Syndication

Page view tracker