除了第3章介绍的基本内容之外,在C#中还提供了很多其他的高级功能,比如反射、组合、多线程编程以及与非控代码的互操作等。本章主要介绍一些常用的面向对象的高级编程技术。
封装、继承与多态性是面向对象编程的三大原则。封装用于隐藏调用者不需要了解的信息;继承则简化了类的设计;多态性是指类为名称相同的方法提供不同实现方式的能力。在实际编程中,只有深刻理解这些概念,才能更好的利用面向对象技术编写出高质量的程序代码。
4.1.1 封装性
在面向对象编程中,封装是指把数据和处理这些数据的代码封装在一个类中,然后通过提供相应的属性和方法供调用者使用,通过隐藏调用者不需要的信息(如实现细节),可以让调用者只关心对象中对其有用的相关内容。
在设计类时,应尽可能隐藏实现的细节,只提供给调用者需要知道的操作和数据。这样做的好处是当设计者修改实现的细节时,可以不影响调用者与类的交互方式。
【例4-1】通过属性进行封装。
using System;
using System.Collections.Generic;
using System.Text;
namespace EncapsulationExample
{
class BankAccount
{
//帐户余额
private decimal accountBalance;
public decimal AccountBalance
{
get
{
return accountBalance;
}
}
public BankAccount(decimal startAmount)
{
accountBalance = startAmount;
}
//可以通过此方法取款
public void Withdraw(decimal money)
{
if (accountBalance >= money)
{
accountBalance -= money;
}
}
}
class Program
{
static void Main(string[] args)
{
BankAccount ZhangSan = new BankAccount(1000);
ZhangSan.Withdraw(100);
Console.WriteLine("帐户的余额为{0:C}", ZhangSan.AccountBalance);
Console.ReadLine();
}
}
}
这是对银行账户余额accountBalance进行封装的一个例子。在这个例子中,银行帐户ZhangSan可以通过AccountBalance属性读取账户余额accountBalance的值,但不能直接读取或修改私有成员accountBalance的值。另外,由于AccountBalance属性只提供了get操作,因此也无法通过该属性修改accountBalance的值,从而避免了直接修改余额引起不正确的结果。
4.1.2 继承
继承(Inheritance)是指类能够从它的父类中继承除构造函数以外的所有数据的定义和功能。继承能够提高代码的可重用性。通过继承,可以使程序员能够直接享用他人或自己事先写好的基类中已有的功能,而不必全部重新编写。
在C#中,作为基础的、被继承的类称为基类(Base Class),继承自别的类的子类称为扩充类(Derived Class,又叫派生类)。
C#语言提供了两种实现继承的方式:类继承和接口继承。不过类继承只允许单一继承,即只有一个基类。单一继承已经能够满足大多数面向对象应用程序开发上的要求,也有效的降低了复杂性。如果必须使用多重继承,可以通过接口来实现。

注意,虽然继承是非常有用的编程概念,但使用不当也会带来一些负面的效果。下列情况下可以使用类继承:
1) 扩充类与基类的关系是“属于”关系而不是“具有”关系,或者说,扩充类不能是基类的“子集”,也不能是只包含基类中的一部分。“具有”关系的类不适合使用类继承,因为这样可能会继承不适当的属性和方法。图4-1说明了“属于”关系和“具有”关系的区别。
2) 可以重用基类的代码。例如,如果一个数据库中有多个表,对每一个表都设计添加、删除、修改等功能显然既费时又容易出错,这时使用类继承就是比较好的选择。
3) 需要将相同的类和方法应用到不同的数据类型。这时可以利用重写基类中的某些方法来实现。
4) 类层次分级比较少,而且其他开发人员不可能添加太多的级别。继承最适合于分级相对较少的类层次结构。一般来说,应将层次结构限制在低于六级。
5) 需要只修改基类就可以对继承的类进行全部更改的情况。继承的一个最强大的功能是在基类中进行的更改将自动传播到派生类中。例如更新一个方法的实现,从而几十甚至上百个派生类都可以自动使用该新代码。但是,一般情况下,应避免更改基类成员的名称或类型,因为这样容易导致使用原成员的扩充类出现问题。