using System;
class A {
public void F() { Console.WriteLine("A.F"); }
public virtual void G() { Console.WriteLine("A.G"); }
}
class B : A {
new public void F() { Console.WriteLine("B.F"); }
public override void G() { Console.WriteLine("B.G"); }
}
class Test {
static void Main() {
B b = new B();
A a = b;
a.F();
b.F();
a.G();
b.G();
}
}
这是今天看书的一段代码,mark!
在该示例中,A 引入一个非虚方法 F 和一个虚方法 G。类 B 引入一个新的非虚方法 F,从而隐藏了继承的 F,并且还重写了继承的方法 G。根据对象初始化的过程,着眼于方法表的顺序:关注对象,从自身类向派生类搜索到第一个可以访问的同名方法。“在虚方法调用中,该调用所涉及的那个实例的运行时类型 (run-time type) 确定了要被调用的究竟是该方法的哪一个实现。在非虚方法调用中,相关的实例的编译时类型 (compile-time type) 是决定性因素。”(——《C#规范4.0》)由此可知,实例a b内存的布局前半部分是一致的,运行时类型b:B.F B.G 。编译类型a:A.F
对调用哪个实际方法实现起决定作用的是该实例的运行时类型(即引用类型 A),而不是该实例的编译时类型(即实际内存分配对象B)。
class A {
public virtual void F() { Console.WriteLine("A.F"); }
}
class B : A {
public override void F() { Console.WriteLine("B.F"); }
}
class C : B {
new public virtual void F() { Console.WriteLine("C.F"); }
}
class D : C {
public override void F() { Console.WriteLine("D.F"); }
}
class Test {
static void Main() {
D d = new D();
A a = d;
B b = d;
C c = d;
a.F();
b.F();
c.F();
d.F();
}
}
C 类和 D 类包含两个具有相同签名的虚方法:一个是 A 引入的,另一个是 C 引入的。但是,由 C 引入的方法隐藏了从 A 继承的方法。因此,D 中的重写声明所重写的是由 C 引入的方法,D 不可能重写由 A 引入的方法。内存都D的布局,a.F搜索到B类有满足方法,b.F也是直接该类满足,c和d同理。此例产生输出:B.F B.F D.F D.F。