C# 8.0本质论
上QQ阅读APP看书,第一时间看更新

第6章 类

第1章简单介绍了如何声明一个名为HelloWorld的新类。第2章介绍了C#内置的基元类型。学习了控制流程以及如何声明方法之后,就可以学习如何定义自己的类型了。这是任何C#程序的核心构造。正是由于C#支持类以及根据类来创建对象,所以我们说C#是一种面向对象的语言。

本章介绍C#面向对象编程的基础知识。重点在于如何定义,可将类理解成对象的模板。

在面向对象编程中,之前学过的所有结构化的、基于控制流的编程构造仍然适用。但将那些构造封装在类中,可以创建更大、更有条理以及更容易维护的程序。从结构化的、基于控制流程的程序转向面向对象的程序,是因为面向对象编程提供了一个额外的组织层次。结果是较小的程序在某种程度上得到了简化。但更重要的是,现在更容易创建较大的程序,因为程序中的代码得到了更好的组织。

面向对象编程的一个关键优势是不必从头创建新程序,而是可以将现有的一系列对象组装到一起,用新功能扩展类,或添加更多的类。

还不熟悉面向对象编程的读者应阅读“初学者主题”获得对它的初步了解。“初学者主题”以外的内容将着重讨论如何使用C#进行面向对象编程,并假定读者已熟悉了面向对象思维模式。

为支持封装,C#必须支持类、属性、访问修饰符以及方法等构造。本章着重讨论前三种,方法已在第5章讨论。掌握这些基础知识之后,第7章将讨论如何通过面向对象编程实现继承和多态性。

初学者主题:面向对象编程(OOP)

如今,成功编程的关键在于提供恰当的组织和结构,以满足大型应用程序的复杂需求。面向对象编程能很好地实现该目标。有多好呢?可以这样说,开发者一旦熟悉了面向对象编程,除非写一些极为简单的程序,否则很难回到结构化编程。

面向对象编程最基本的构造是类。一组类构成了编程抽象、模型或模板,通常对应现实世界的一个概念。例如,OpticalStorageMedia(光学存储媒体)类可能有一个Eject()方法,用于从播放机弹出光盘。OpticalStorageMedia类是现实世界的CD/DVD播放机对象的编程抽象。

类是面向对象编程的三个主要特征——封装、继承和多态性——的基础。

封装

封装旨在隐藏细节。必要的时候细节仍可访问,但通过巧妙地封装细节,大的程序变得更容易理解,数据不会被不慎修改,代码也变得更容易维护(因为对一处代码进行修改所造成的影响被限制在封装的范围之内)。方法就是封装的一个例子。虽然可以将代码从方法中拿出直接嵌入调用者的代码中,但将特定的代码重构成方法,能享受到封装所带来的好处。

继承

考虑这个例子:DVD是光学存储媒体的一个类型。它具有特定的存储容量,能容纳一部数字电影。CD也是光学存储媒体的一个类型,但它具有不同特征。CD上的版权保护有别于DVD的版权保护,两者存储容量也不同。无论是CD还是DVD,它们都有别于硬盘、U盘和软盘。虽然所有这些都是“存储媒体”,但分别具有不同的特征——即使一些基本功能也是不同的,比如所支持的文件系统,以及媒体的实例是只读的还是可读可写的。

面向对象编程中的继承允许在这些相似但又不同的物件之间建立“属于”(is a)关系。可合理地认为DVD和CD都“属于”存储媒体。因此它们都具有存储能力。类似地,CD和DVD都“属于”光学存储媒体,后者又“属于”存储媒体。

为上面提到的每种存储媒体类型都定义一个类,就得到一个类层次结构,它由一系列“属于”关系构成。例如,可将基类型(所有存储媒体都从它派生)定义成StorageMedia(存储媒体)。CD、DVD、硬盘、U盘和软盘都属于StorageMedia。但CD和DVD不必直接从StorageMedia派生。相反,可从中间类型OpticalStorageMedia(光学存储媒体)派生。可用一幅UML(Unified Modeling Language,统一建模语言)风格的类关系图来查看类层次结构,如图6.1所示。

图6.1 类层次结构

继承关系至少涉及两个类,其中一个是另一个更具体的版本。图6.1中的HardDrive是更具体的StorageMedia。反之不成立,因为StorageMedia的一个实例并非肯定是HardDrive。如图6.1所示,继承涉及的类可能不止两个。

更具体的类型称为派生类型子类型。更常规的类型称为基类型或者超类型。也经常将基类型称为“父”类型,将派生类型称为它的“子”类型。虽然这种说法很常见,但会带来混淆。“子”毕竟不是一种“父”!本书将采用“派生类型”和“基类型”的说法。

为了从一个类型派生继承,需对类型进行特化,这意味着要对基类型进行自定义,为满足特定需求而调整它。基类型可能包含所有派生类型都适用的实现细节。

继承最关键的一点是所有派生类型都继承了基类型的成员。派生类型中可以修改基类型的成员,但无论如何,派生类型除了自己显式添加的成员,还包含了基类型的成员。

可用派生类型以一致性的层次结构组织类。在这个层次结构中,派生类型比它们的基类型更特别。

多态性

多态性这个词由一个表示“多”(poly)的词根和一个表示“态”(morph)的词根构成。讲到对象时,多态性意味着一个方法或类型可具有多种形式的实现。假定有一个媒体播放机,它既能播放音乐CD,也能播放包含MP3歌曲的DVD。但Play()方法的具体实现会随着媒体类型的变化而变化。在一个音乐CD对象上调用Play()方法,或者在一张音乐DVD上调用Play()方法,都能播放出音乐,因为每种类型都理解自己具体如何“播放”。媒体播放机唯一知道的就是公共基类型OpticalStorageMedia以及它定义了Play()方法签名的事实。多态性使不同类型能自己处理一个方法的实现细节,因为多个派生类型都包含了该方法,每个派生类型都共享同一个基类型(或接口),后者也包含了相同的方法签名。