6.6.3 对类型进行版本控制时的虚方法的处理
如果类型要作为基类型使用,增加或修改它的成员时务必非常小心。
隐藏基类的同名实例方法
假定CompanyA定义了Phone类型
namespace CompanyA { public class Phone { public void Dial { Console.WriteLine("Phone.Dial"); //在这里执行拨号操作 } } }
再假定CompanyB定义了BetterPhone类型,使用CompanyA的Phone类型作为基类型
namespace CompanyB { public class BetterPhone:CompanyA.Phone { public void Dial { Console.WriteLine("BetterPhone.Dial"); EstablishConnection(); base.Dial(); } protected virtual void EstablishConnection() { Console.WriteLine("BetterPhone.EstablishConnection"); //在这里执行建立连接的操作 } } }
CompanyB编译上述代码时,C#编译器生成以下警告消息:
warning CS0108:"CompanyB.BetterPhone.Dial()"隐藏了继承的成员"COmpanyA.Phone.Dial"。
如果是有意隐藏,请使用关键字new
该警告告诉开发人员,BetterPhone类正在定义这个Dial方法,他会隐藏Phone类定义的Dial。
解决办法就是在BetterPhone类中定义Dial时,在前面加一个new关键字。
以下是改正后的BetterPhone类
namespace CompanyB { public class BetterPhone:CompanyA.Phone { public new void Dial { Console.WriteLine("BetterPhone.Dial"); EstablishConnection(); base.Dial(); } protected virtual void EstablishConnection() { Console.WriteLine("BetterPhone.EstablishConnection"); //在这里执行建立连接的操作 } } }
现在CompanyB能正常使用BetterPhone.Dial。
以下是CompanyB可能写的一些示例代码。
public sealed class Program() { public static void Main { CompanyB.BetterPhone phone=new CompanyB.BetterPhone(); phone.Dial(); } }
运行以上代码,输出结果如下所示
BetterPhone.Dial
BetterPhone.EstablishConnection
Phone.Dial
隐藏基类同名的虚方法
现在,假定几家公司计划使用CompanyA的Phone类型,再假定这几家公司都认为在Dial方法中建立连接的主意非常好。CompanyA决定对这个Phone类进行修订。
namespace CompanyA { public class Phone { public void Dial { Console.WriteLine("Phone.Dial"); EstablishConnection(); //在这里执行拨号操作 } protected virtual void EstablishConnection() { Console.WriteLine("Phone.EstablishConnection"); //在这里执行建立连接的操作 } } }
现在,一旦CompanyB编译他的BetterPhone类型就会生成以下警告
warning CS0114:"CompanyB.BetterPhone.EstablishConnection()"
将隐藏继承的成员"CompanyA.Phone.EstablishConnection()"。
若要使当前成员重写该类型,请天假关键字override否则,添加关键字new。
如果CompanyB认定EstablishConnection方法在两个类型的语义不一致,
CompanyB可以告诉编译器使用BetterPhone类中定义的Dial和EstablishConnection方法,他们与基类型Phone中的没有关系。
CompanyB可以为EstablishConnection方法添加new关键字来告诉编译器这一点。
namespace CompanyB { public class BetterPhone:CompanyA.Phone { public new void Dial { Console.WriteLine("BetterPhone.Dial"); EstablishConnection(); base.Dial(); } protected new virtual void EstablishConnection() { Console.WriteLine("BetterPhone.EstablishConnection"); //在这里执行建立连接的操作 } } }
在这段代码中,关键字new告诉编译器生成元数据,让CLR知道BetterPhone类型的EstablishConnection方法应被视为由BetterPhone类型引入的新函数。
执行相同的应用程序代码,输出结果如下所示
BetterPhone.Dial
BetterPhone.EstablishConnection
Phone.Dial
Phone.EstablishConnection
如果更改方法名只会造成源代码发生适度更新,就应该更改方法名,避免Dial和EstablishConnection方法的两种不同含义是开发人员产生混淆。
将派生类虚方法改为重写基类的方法,实例方法继承自基类
还有一个办法是,CompanyB可以获得CompanyA的新版本Phone类型,并确定Dial和EstablishConnection在Phone中的语义正好是他们所希望的。
这种情况,CompanyB可以通过完全移除Dial方法来修改他们的BetterPhone类型。另外,由于CompanyB希望告诉编译器两类型的EstablishConnection方法是相关的,必须移除new关键字。为了准确表达意图,CompanyB的开发人员必须将BetterPhone的EstablishConnection方法由virtual改编为override。以下代码展示了新版本的BetterPhone类。
namespace CompanyB { public class BetterPhone:CompanyA.Phone { //删除Dial方法,从基类继承 protected override void EstablishConnection() { Console.WriteLine("BetterPhone.EstablishConnection"); //在这里执行建立连接的操作 } } }
执行相同的应用程序代码,输出结果如下所示。
Phone.Dial
BetterPhone.EstablishConnection