科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件漫谈C#编程中的多态与new关键字

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

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

多态就是使得你能够用一种统一的方式来处理一组各具个性却同属一族的不同个体的机制。

作者:allenlooplee 来源:博客园 2007年11月11日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
5. 谁的问题?

  我必须指出,如果 Base(Code #01)和 Derived2(Code #04)同时存在的话,它们俩其中一个存在着设计上的问题。为什么这样说呢?Base 的设计者在 PrintStatus 上应用 virtual 说明了他希望派生类能透过重写这一方法来参与动态绑定,即多态性;而 Derived2 的设计者在 PrintStatus 上应用 new 则说明了他希望割断 Derived2.PrintStatus 和 Base.PrintStatus 之间的联系,这将使得 Derived2.PrintStatus 无法参与到 Base 的设计者所期望的动态绑定中。如果在 Base.PrintStatus 上应用 virtual(即对多态性的期望)是合理的话,那么 Derived2.PrintStatus 应该换用另外一个名字了;如果在 Derived2.PrintStatus 上应用 new(即否决参与动态绑定)是合理的,那么 Base.PrintStatus 应该考虑是否去掉 virtual 了,否则就会出现一些奇怪的行为,例如 Output #01 的第三行输出。

  假如继承体系中多态性行为的期望是合理的话,那么更实际的做法应该是把 Base 定义成这样:

// Code #12

abstract class Base
{
 public abstract void PrintStatus();
}

  而原来 Base 中的实现应该下移到一个派生类中: // Code #13

class Derived3 : Base
{
 public override void PrintStatus()
 {
  Console.WriteLine("public override void PrintStatus() in Derived3 [originally implemented in Base]");
 }
}

  这样,Derived2.PrintStatus 将使得编译无法完成,从而迫使其设计者要么更改方法的名字,要么换用 override 修饰。这种强制使得 Derived2 的设计者不得不重新考虑其设计的合理性。

  假如继承体系中多态性行为的期望不总是合理呢?例如 Stream 有这样一个方法:

public abstract long Seek(long offset, SeekOrigin origin);

  现在假设我有一个方法在处理输入流时需要用到 Stream.Seek:

// Code #14

public void Resume(Stream input, long offset)
{
 //
 input.Seek(offset, SeekOrigin.Begin);
 //
}

  当我们向 Resume 传递一个 NetworkStream 的实例,Resume 将会抛出一个 NotSupportedException,因为 NetworkStream 不支持 Seek。那么这是否说明 Stream 的设计有问题呢?

  设想 Resume 是一个下载工具进行断点续传的方法,然而,并不是所有的服务器都支持断点续传的,于是,你需要首先判断输入流是否支持 Seek 操作,再决定如何处理输入流:

// Code #15

public void Resume(Stream input, long offset)
{
 if (input.CanSeek)
 {
  //
  input.Seek(offset, SeekOrigin.Begin);
  //
 }
 else
 {
  //
 }
}

  如果 CanSeek 为 false,那就只好从头来过了。

  实际上,我们并不能保证任何 Stream 的派生类都能够支持某个(些)操作,我们甚至不能保证来自同一个派生类的所有实例都支持某个(些)操作。你可以设想有这样一个 PriorityStream,它能够根据当前登录账号的权限来决定是否提供写操作,这使得拥有足够权限的人才能修改数据。或许 Stream 的设计者已经预料到这类情况的发生,所以 CanRead、CanSeek 和 CanWrite 就被加入到 Stream 里了。

  值得注意的是,Code #07 的 Derived2 可能是一个很糟糕的设计,也可能是一个很实用的设计。在本文,它是一个很糟糕的设计,如果你足够细心,你会察觉到 Derived2 的设计者希望 Derived2.PrintStatus 绕过 Base.PrintStatus 而直接和 IFace.PrintStauts 进行关联,表面上这没什么不妥,但实质上 Base.PrintStatus 和 IFace.PrintStauts 在约定上是同质的,这意味着如果与 IFace.PrintStauts 进行关联就等于承认自己和 Base.PrintStatus 是同质的,这样的话,为什么不直接在 Derived2 里重写 PrintStatus 呢?在《基类与接口混合继承的声明问题》中,我示范了一个实用的设计,用 new 和接口重新实现(Interface reimplementation)来纠正非预期的多态行为。

  6. 最后...

  当我的朋友拿着问题来找我时,我通常都不会直接给出我的答案,而是尽我的能力向他提供足够多的可用信息,以便他能够根据他所面临的实际情况作出处理,毕竟,我不会比他更了解他的问题,而他也应该形成他自己的关于他的问题的思考。我希望浪子能用自己的答案回答他所提出的问题,因为只有这样,那些知识才真正属于他,并且我也相信本文已经提供了足够多的可用信息。

查看本文来源

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章