面向对象的程序设计有三大要素封装继承和多态虚函数是多态的重要组成部分同时又在类的继承关系中有着很多变化本文讨论NET中对虚函数的支持
首先我们通过一个例子来看看虚函数的普通用法
class CA {
public virtual void Foo() {
ConsoleWriteLine(CAFoo);
}
}
class CB : CA{
public override void Foo() {
ConsoleWriteLine(CBFoo);
}
}
class Test{
public static void InvokeFoo(CA ca) {
caFoo();
}
public static void Main(){
InvokeFoo(new CB());
}
}
输出结果
CBFoo
在这个例子中尽管在调用InvokeFoo()的时候CB被转换成CA但是当执行caFoo的时候仍然调用了CB的Foo因为ca此时指向的是一个CB类型的对象这种调用模式我们称之为运行时绑定因为在编译InvokeFoo时编译器无法获取参数ca的真实类型只有在运行的时候才能根据ca的真实类型决定调用哪一个函数
在这个例子中两个关键字值得我们注意首先是virtual他告诉编译器当前函数需要运行时绑定其次是override他告诉编译器我要覆盖基类中的Foo()
看到这里可能读者会对两个问题持有疑惑
[问题]: 不用virtual结果如何?
[问题]: 不用override结果如何?
读者不妨自己动手修改上例尝试这两个关键字的不同组合看看输出的结果如何在这里我仅给出组合条件和其输出结果
序号 基类(CA)中是否有virtual 子类(CB)中是否有override 输出
是 是 CBFoo
是 否 CAFoo
否 是 编译错误
否 否 CAFoo
我希望通过对这组实验结果的解释交待一些NET中虚函数的相关概念
运行时绑定仅体现在虚函数中因此在试验中输出的结果是CAFoo因为Foo没有被申明为virtual在编译阶段已经把caFoo绑定到CAFoo
Override只能用于虚函数中当子类继承基类他便拥有了基类所有的函数Override修饰的函数将替换基类原来的函数否则子类会新增加一个函数并同时保留基类中的函数 下面的这个例子很好的说明了这个问题
class CA{
public virtual void Foo(){
ConsoleWriteLine(CAFoo);
}
}
class CB : CA{
public override void Foo() {
ConsoleWriteLine(CBFoo);
}
}
class CC : CA {
public new void Foo() {
ConsoleWriteLine(CCFoo);
}
}
class Test {
public static void Main() {
ConsoleWriteLine(typeof(CB)GetMethods()Length); // 输出
ConsoleWriteLine(typeof(CC)GetMethods()Length);// 输出
}
}
这段程序输出CB和CC的函数个数CB的个函数中个来自于SysetmObject剩下的一个就是FooCC中多了一个函数因为使用了new (如果不使用new也是相同的结果因为C#编译器默认使用new但不显示指明new会给出一个警告)说明了CCFoo是一个不同于CAFoo的虚函数