漫谈C#编程中的多态与new关键字

来源:网络整理 责任编辑:栏目编辑 发表时间:2013-07-01 20:24 点击:
 

  1. 你通常怎样用多态?

  假设我有一个类,里面有一个 PrintStatus 方法,用于打印实例的当前状态,我希望该类的派生类都带有一个 PrintStatus 方法,并且这些方法都用于打印其实例的当前状态。那么我会这样表达我的愿望:

// 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 overri

    发表评论
    请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
    用户名: 验证码:点击我更换图片
    最新评论 更多>>

    推荐热点

    • 用C#制作屏幕捕获程序
    • .NET程序员项目开发必知必会—Dev环境中的集成测试用例执行时上
    • 遍历ArrayList易犯错误
    • C#对XML操作:一个处理XML文件的类(1)
    • .NET简谈反射(动态调用)
    • 使用C#编写LED样式时钟控件
    • DataList嵌套问题 如何删除内层子DataList的记录
    • 怎样用C#实现完整文档打印功能
    • .NET简谈自定义事务资源管理器
    网站首页 - 友情链接 - 网站地图 - TAG标签 - RSS订阅 - 内容搜索
    Copyright © 2008-2015 计算机技术学习交流网. 版权所有

    豫ICP备11007008号-1