漫谈C#编程中的多态与new关键字
发表于:2007-06-21来源:作者:点击数:
标签:
下一页 1 2 1. 你通常怎样用多态? 假设我有一个类,里面有一个 PrintStatus 方法,用于打印实例的当前状态,我希望该类的派生类都带有一个 PrintStatus 方法,并且这些方法都用于打印其实例的当前状态。那么我会这样表达我的愿望: // Code #01 class Base
下一页 1 2
1. 你通常怎样用多态?
假设我有一个类,里面有一个 PrintStatus 方法,用于打印实例的当前状态,我希望该类的派生类都带有一个 PrintStatus 方法,并且这些方法都用于打印其实例的当前状态。那么我会这样表达我的愿望:
clearcase/" target="_blank" >cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
// Code #01
class Base { public virtual void PrintStatus() { Console.WriteLine("public virtual void PrintStatus() in Base"); } } | 于是我可以写一个这样的方法:
// Code #02
public void DisplayStatusOf(Base[] bs) { foreach (Base b in bs) { b.PrintStatus(); } } | bs 中可能包含着不同的 Base 的派生类,但我们却可以忽略这些“个性”而使用一种统一的方式来处理某事。在 .NET 2.0 中,XmlReader 的 Create 有这样一个版本:
public static XmlReader Create(Stream input); | 你可以向 Create 传递任何可用的“流”,例如来自文件的“流”(FileStream)、来自内存的“流”(MemoryStream)或来自网络的“流”(NetworkStream)等。虽然每一中“流”的工作细节都不同,但我们却使用一种统一的方式来处理这些“流”。
2. 假如有人不遵守承诺...
DisplayStatusOf 隐含着这样一个假设:bs 中如果存在派生类的实例,那么该派生类应该重写 PrintStatus,当然必须加上 override 关键字:
// Code #03
class Derived1 : Base { public override void PrintStatus() { Console.WriteLine("public override void PrintStatus() in Derived1"); } } | 你可以把这看作一种承诺、约定,直到有人沉不住气...
// Code #04
class Derived2 : Base { public new void PrintStatus() { Console.WriteLine("public new void PrintStatus() in Derived2"); } } | 假设我们有这样一个数组: // Code #05
Base[] bs = new Base[] { new Base(), new Derived1(), new Derived2() }; | 把它传递给 DisplayStatusOf,则输出是:
// Output #01
// public virtual void PrintStatus() in Base // public override void PrintStatus() in Derived1 // public virtual void PrintStatus() in Base | 从输出结果中很容易看出 Derived2 并没有按照我们期望的去做。但你无需惊讶,这是由于 Derived2 的设计者没有“遵守约定”的缘故。
3. new:封印咒术
new 似乎给人一种这样的感觉,它的使用者喜欢打破别人的约定,然而,如果使用恰当,new 可以弥补基类设计者的“短见”。在 Creating a Data Bound ListView Control 中,Rockford Lhotka 就示范了如何封印原来的 ListView.Columns,并使自行添加的返回 DataColumnHeaderCollection 的 Columns 取而代之。
从 Output #01 中我们可以看到,new 只是把 Base.PrintStatus 封印起来而不是消灭掉,你可以解除封印然后进行访问。对于 Derived2 的使用者,解封的方法是把 Derived2 的实例转换成 Base 类型:
// Code #06
Base d2 = new Derived2(); d2.PrintStatus();
// Output #02
// public virtual void PrintStatus() in Base 而在 Derived2 内部,你可以透过 base 来访问:
// Code #07
base.PrintStatus(); | 这种方法是针对实例成员的,如果被封印的成员是静态成员的话,就要透过类名来访问了。
4. 假如 Base.PrintStatus 是某个接口的隐式实现...
假如 Base 实现了一个 IFace 接口:
// Code #08
interface IFace { void PrintStatus(); }
class Base : IFace { public virtual void PrintStatus() { Console.WriteLine("public virtual void PrintStatus() in Base"); } } | 我们只需要让 Derived2 重新实现 IFace:
// Code #09
class Derived2 : Base, IFace { public new void PrintStatus() { Console.WriteLine("public new void PrintStatus() in Derived2"); } } | Derived1 保持不变。则把:
// Code #10
IFace[] fs = new IFace[] { new Base(), new Derived1(), new Derived2(), } | 传递给:
// Code #11
public void DisplayStatusOf(IFace[] fs) { foreach (IFace f in fs) { f.PrintStatus(); } } | 输出结果是:
// Output #03
// public virtual void PrintStatus() in Base // public override void PrintStatus() in Derived1 // public new void PrintStatus() in Derived2 | 从输出结果中,我们可以看到,虽然 Derived2.PrintStatus 应用了 new,但却依然参与动态绑定,这是由于 new 只能割断 Derived2.PrintStatus 和 Base.PrintStatus 的联系,而不能割断它与 IFace.PrintStatus 的联系。我在 Derived2 的定义中重新指定实现 IFace,这将使得编译器认为 Derived2.PrintStatus 是 IFace.PrintStatus 的隐式实现,于是,在动态绑定时 Derived2.PrintStatus 就被包括进来了。
|
原文转自:http://www.ltesting.net
- 评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
-