A question about understand inheritance, virtual and override

T

Tony Johansson

Hello!!

Below I have three classes. They are Base, Sub and Test.
Sub is inheriting from Base.

The main issue here is to pass the reference of Test up to the base class
Base.
This work but I hope that somebody can explain more about why this works.

The sequence is the following.
1. Sub is being instansiated from main.
2. c-tor Sub is calling reInit() which is inherited from Base.
3. Method getTest() is being called from reInit which exist in Base.
4. Property Tal is being called from reInit and the int tal is being written
to console.

Now what I find a little hard to understand is how it is possible to call a
method which is below the class you are within.
In this case you are in class Base and call getTest which exist in Base so
why is not the getTest
which exist in Base called instead.

If you for example call getTest() from Sub c-tor I can understand that the
overridden getTest in Sub is called insteadd of getTest in Base.

But when you call methods in Base that also exist in Sub I don't find it so
easy to understand it.

I also know that the keyword virtual and override that make this possible.

I hope somebody can give some good explanation so I don't find it hard to
understand.


public class Base
{
public virtual Test getTest()
{
return null;
}

public void reInit()
{
Console.WriteLine("t.Tal={0}",getTest().Tal);
}
}
public class Sub : Base
{
private Test test = new Test();

public Sub()
{
reInit();
}

public override Test getTest()
{
return test;
}

static void Main()
{
Sub sub = new Sub();
}
}

public class Test
{
private int tal = -1;

public int Tal
{
get {return tal;}
}
}


//Tony
 
B

Barry Kelly

Tony Johansson said:
Now what I find a little hard to understand is how it is possible to call a
method which is below the class you are within.

This behaviour is called dynamic dispatch and is a fundamental part of
object orientation. The actual method being invoked for virtual methods
is based on the runtime type of the instance rather than the static,
compile-time type.

When you call a virtual method on an instance, where that instance is
statically declared to be of a type anywhere in the type hierarchy where
that method is visible, that call is dispatched dynamically at runtime
to the most derived override of that method based on the actual runtime
type of the instance on which the method was invoked.

In short, whenever you call an unsealed virtual method on an unsealed
public class (i.e. so the method is eligible for overriding by third
parties), you have no idea what code will be executed, because
descendant classes get first chance to intercept that call.

For that reason, it's important that the base class's expectations of
the behaviour of overridden virtual methods is well defined in the
specification of the base class.
In this case you are in class Base and call getTest which exist in Base so
why is not the getTest
which exist in Base called instead.

If you want that behaviour, then don't declare the method getTest() as
virtual.

-- Barry
 
B

Bruce Wood

In this case you are in class Base and call getTest which exist in Base so
why is not the getTest which exist in Base called instead.

To understand what's happening here it's very important to see the code
in two different ways. It's important to distinguish between _compile
time_ and _run time_.

When you read the code that you have written, in the way that you've
organized it "on the page" as it were, you're looking at the code from
the compiler's point of view. From that point of view you're right: the
reInit() method is written in the code that makes up the definition of
the Base class.

However, there is another way of looking at the code: the way that the
runtime sees it.

When you instantiate an object, say a Sub, that object is of a type. In
this case, you instantiate a new object of type Sub and put a reference
to it in the variable "sub". That object you created will always be of
type Sub; its type can never change. Even if you were to say this:

Sub mySub = new Sub()
Base myBase = mySub;

which is legal, myBase would now refer to an object of type Sub,
because it was created as a Sub ("new Sub()") and it can never change
its type.

So, from the runtime's point of view, the fact that reInit() is defined
in the code that defines the Base class is irrelevant. All that matters
is that the object that is executing the reInit method is an object of
type Sub.

Now, finally, realize that the compiler doesn't resolve overrides: it
leaves them for the runtime to resolve. So, when the compiler sees a
virtual / overridden method, it sets up a few things, but leaves it for
the runtime to decide which method, at what level in the class
hierarchy, should really be called. In this case, because the object
making the call was instantiated as type Sub ("new Sub()"), the runtime
decides to call the getTest() method in Sub.

(Finally, note that _overrides_ are different from _overloads_: if you
were to declare more than one method with the same name but different
parameters... in that case it _would be_ the compiler that decided
which one should be called. It is only if you override methods... have
methods at different levels of the class hierarchy with the same name
and same parameters... that the runtime is left to make the decision.)

So, when you're looking at your code, you have to keep to the two
points of view in mind: how the compiler sees things (which is usually
the way you naturally read them) and how the runtime sees things (which
all has to do with what kind of object is making the call).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top