Virtual functions in C#
public class Base1
{
public virtual void f()
{
Console.WriteLine("Base1.f()");
}
}
public class Derived1 : Base1
{
// Hides Base1.f() because 'override' was not specified
public new virtual void f()
{
Console.WriteLine("Derived1.f()");
}
}
public class Derived2 : Derived1
{
// Overrides Derived1.f()
public override void f()
{
Console.WriteLine("Derived2.f()");
// Call base method
base.f();
}
}
class Program
{
static void Main(string[] args)
Bas开发者_C百科e1 ob1 = new Derived1();
ob1.f();
Base1 ob2 = new Derived2();
ob2.f();
Derived1 ob3 = new Derived2();
ob3.f();
Derived2 ob4 = new Derived2();
ob4.f();
}
}
// Calls Derived2.f() because Derived2 overrides Derived1().f()
Derived1 ob3 = new Derived2();
ob3.f();
it was expecting that the
Base1 ob2 = new Derived2();
ob2.f();
- The derived2 function will be called but the base class function was called , what is the reason for this.
- Does .net uses vtables
The method slot used by static analysis during compilation depends on the type of the variable (or expression), not the actual object. The variable ob2
is typed as Base1
, so the Base1
method slot is used. And then the correct override is selected based on the typed (essentially vtable on that slot). So the base function is used.
To use the derived2 function, the variable (or expression) must be typed as Derived1
or a subclass.
Basically, if the compile-time type of the variable you're using to call f()
is Base1
, it will call the base method - because nothing's actually overriding it.
If the compile-time type is Derived1
or Derived2
, it will call the appropriate method in either Derived1
or Derived2
based on the execution-time type of the object... because at that point the compiler will just be issuing a virtual call to Derived1.f()
, and overriding will occur at execution time.
And yes, .NET uses vtables.
The problem here is that you're mixing things a bit too much.
Basically, here's what you have done:
- You define a virtual method
f
in the base class - You descend from that base class, and create a new virtual
f
method - You descend from the second class, and override
f
, this overrides the one from the second class, not the one in the base class.
So, when you say:
Base1 b = new Derived2();
b.f();
then you're always (in this case) going to call the base implementation of f
, since the overridden f
in Derived2
is a different f
method. The name is the same, but it is still a different method.
The reason for this is that the compiler will see that the f
you're calling is the one coming from the Base1
class, and so it will call that.
Since no class overrides Base1.f
, that's the one you're calling.
In response to the question in the comment, strictly speaking, the class will have two virtual methods, both named f.
One, however, is shadowed by the new one introduced in Derived1.
You can, inside the class, pick which to call:
public void MethodInDerived1()
{
f(); // calls Derived1.f()
base.f(); // calls Base1.f()
}
From the outside, however, you need to "pick" by casting.
In other words:
Derived1 d = new Derived1();
d.f(); // calls Derived1.f()
((Base1)d).f(); // calls Base1.f()
You can also observe the methods through reflection. If you execute the following code in LINQPad, you will see that there are two methods both named f
:
void Main()
{
typeof(Derived1).GetMethods().Dump();
}
public class Base1
{
public virtual void f()
{
Debug.WriteLine("Base1.f");
}
}
public class Derived1 : Base1
{
public virtual new void f()
{
Debug.WriteLine("Derived1.f");
}
}
public class Derived2 : Derived1
{
public override void f()
{
Debug.WriteLine("Derived2.f");
base.f();
}
}
The output from this script, truncated (there's more info out to the right):
精彩评论