Previously I have asked a question that was answered not fully, hence I have decided to re-formulate my question to understand what is going on:
Here is my class hierarchy:
interface I
{
void f();
}
class A : I
{
// non virtual method
public void f()
{
Debug.Log("---->> A ");
}
}
class B : A
{
// non overriding but hiding class A method
public void f()
{
Debug.Log("---->> B ");
}
}
class C : I
{
// non virtual method
public void f()
{
Debug.Log("---->> C ");
}
}
Here is the execution code:
Random rnd = new Random();
var randomI = rnd.Next(0, 2);
I i = null;
if (randomI == 0)
{
i = new B();
}
else
{
i = new C();
}
i.f();
As it is right now, it will either output A or C. It won't output B.
Here is the question: Could you please explain how the decision is made what function to call by covering these steps?
A function call is a request made by a program or script that performs a predetermined function. In the example below, a batch file clears the screen and then calls another batch file. @echo off. cls.
Calling and Called Function ? The Function which calls another Function is called Calling Function and function which is called by another Function is call Called Function. How does Function execution work? A stack data structure is used during the execution of the function calls.
Any parameters that the function is expecting are pushed onto the stack frame. They're pushed onto the stack frame in reverse order that they were declared in the called functions parameter list. The return address of the caller function is pushed onto the stack.
The call() method is a predefined JavaScript method. It can be used to invoke (call) a method with an owner object as an argument (parameter). With call() , an object can use a method belonging to another object.
During compile-time it binds a call to I
interface, and then during run-time it calls a top method in inheritance chain which implements I.f()
.
So, in your code this
A a = new A();
a.f();
B b = new B();
b.f();
will make compiler do the following instructions:
which results into "A" and "B".
However, when you make this:
I i;
i = new B();
i.f();
you make it compile the following instructions:
B
does not implement interface I
At i.f()
line it doesn't know that new B()
was assigned to i
, it could be passed from somewhere else. It just knows that there is some abstract object instance which implements I
and it needs to call its f()
method.
You can think of new
methods like of methods with different name:
public class B : A
{
// non overriding but hiding class A method
public void anotherName()
{
Debug.Log("---->> B ");
}
}
A a = new A();
a.f();
B b = new B();
b.anotherName();
I i = new B();
i.f(); // this will obviously call the `A.f()` because there is no such method in B
The only difference is that you can't call hidden method for inherited class instance.
When the decision is made what function to call - runtime or compile time?
At compile time the compiler determines that A.f
is the method to call if someone casts B
to I
and calls f
on it.
At runtime it calls that method if an instance of B
(vs say an instance of C
) is involved. In other words, the key decision is being made at compile time.
Note that if the method was virtual
then see @YeldarKurmangaliyev's answer for how it calls the "top method in inheritance chain" (but that isn't the scenario here).
What is the mechanism how to decide what function to call?
The relevant part of the specification is 13.4.5 Interface implementation inheritance:
A class inherits all interface implementations provided by its base classes. Without explicitly re-implementing an interface, a derived class cannot in any way alter the interface mappings it inherits from its base classes.
This is why class B : A
shows A but class B : A, I
shows B. Since with the latter you are explicitly re-implementing the interface.
Example from the specification (which is basically your scenario):
A class inherits all interface implementations provided by its base classes. Without explicitly re-implementing an interface, a derived class cannot in any way alter the interface mappings it inherits from its base classes. For example, in the declarations
interface IControl
{
void Paint();
}
class Control: IControl
{
public void Paint() {...}
}
class TextBox: Control
{
new public void Paint() {...}
}
the Paint method in TextBox hides the Paint method in Control, but it does not alter the mapping of Control.Paint onto IControl.Paint, and calls to Paint through class instances and interface instances will have the following effects
Control c = new Control();
TextBox t = new TextBox();
IControl ic = c;
IControl it = t;
c.Paint(); // invokes Control.Paint();
t.Paint(); // invokes TextBox.Paint();
ic.Paint(); // invokes Control.Paint();
it.Paint(); // invokes Control.Paint();
The specification also talks about using virtual
(which is a more common solution than explicitly specifying that B
implements I
):
However, when an interface method is mapped onto a virtual method in a class, it is possible for derived classes to override the virtual method and alter the implementation of the interface.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With