C#入门经典―― 8.2 OOP技术

8.2.1 接口

接口是把隐式公共方法和属性组合起来,以封装特定功能的一个集合。定义接口后,可以在类中实现它,以便支持接口所指定的所有属性和成员。可以把较一般用途的属性和方法组合到一个接口中,然后在类中使用该接口。

注意,接口不能单独存在。不能像实例化一个类那样实例化接口。另外,接口不能包含实现其成员的任何代码,而只能定义成员本身。实现过程必须在实现接口的类中实现。

支持IDisposable接口的对象必须实现其Dispose()方法,即它们必须提供这个方法的代码。当不再需要某个对象时,调用该方法释放重要的资源,否则该资源会等到垃圾回收调用析构函数时才释放。

C#允许使用一种可以优化使用Dispose()方法的结构。Using关键字可以在代码块中初始化使用重要资源的对象,Dispose()方法会在这个代码块的末尾自动调用,用法如下:

<ClassNme> <VariableName> = new <ClassName>()

….

Using(<VariableName>)

{

….

}

或者把初始化对象<VariableName>作为using语句的一部分:

Using(<ClassName> <VariableName> = new <ClassName>())

{

….

}

在这两种情况下,变量<VariableName>可以在using代码块中使用,并在代码块的末尾自动删除(在代码块执行完毕后,调用Dispose())。

 

8.2.2 继承

继承是OOP最重要的特征之一。任何类都可以从另一个类中继承,即这个类拥有它所继承的类的所有成员。在OOP中,被继承(也称为派生)的类称为父类(也称为基类)。注意C#的对象仅能直接派生于一个基类,当然基类也可以有自己的基类。

继承性可以从一个较一般的基类扩展或创建更多的特定类。例如,考虑一个代表家禽的类。这个类叫做Animal,拥有方法如EatFood()或Breed(),我们可以创建一个派生类Cow,支持所有这些方法,它也有自己的方法,如Moo()和SupplyMilk()。还可以创建另一个派生类Chicken,该类有Cluck()和LayEgg()方法。

在继承一个基类时,成员的可访问性就成了一个重要的问题。派生类不能访问基类的私有成员,但可以访问其公共成员和保护(protected)成员。

除了定义成员的保护级别外,还可以为成员定义其继承行为。基类的成员可以是虚拟的,也就是说,成员可以由派生类重写。派生类可以在基类的基础上提供成员的其他执行代码。这种执行代码不会删除原来的代码,仍可以在类中访问原来的代码,但外部代码只能访问派生类提供的执行代码。如果没有提供其他执行方式,外部代码就访问基类中成员的执行代码。

基类还可以定义为抽象类。抽象类不能直接实例化。要使用抽象类,必须继承这个类,抽象类可以有抽象成员,这些成员在基类中没有执行代码,这些执行代码必须在派生类中提供。抽象基类可以提供成员的实现代码,这是很常见的。不能实例化抽象类,并不意味着不能在抽象类中封装功能。

密封类不能用作基类。

8.2.3 多态性

继承的一个结果是派生于基类的(若干个)类在方法和属性上有一定的重叠。因此,可以使用相同的语法处理从同一个基类继承的不同类的对象。例如,如果基类Animal有一个方法EatFood(),则从派生于它的类Cow和Chicken中调用这个方法,其语法是类似的:

Cow myCow = new Cow();

Chicken myChicken = new Chicken();

myCow.EatFood();

myChicken.EatFood();

多态性则更推进了一步。可以把派生类的变量赋予基类的变量,例如:

Animal myAnimal = myCow;

不需要进行强制类型转换,就可以通过这个变量调用基类的方法:

MyAnimal.EatFood();

结果是调用派生类中的EatFood()执行代码。注意,不能以相同的方式调用派生类上定义的方法,下面的代码不能运行:

myAnimal.Moo();

但是,可以把基类的变量转换为派生类变量,调用派生类的方法,如下所示:

Cow myNewCow = (Cow)myAnimal;

myNewCow.Moo();

如果原始变量的类型不是Cow所继承的基类,这个强制类型转换就会产生一个异常。

注意并不是只有共享同一个父类的类才能利用利用多态性。只要子类和孙子类在继承层次结构中有一个相同的类,它们就可以用相同的方式利用多态性。还要注意,在C#中,所有的类都派生于同一个类object,object是继承层次结构中的根。

接口的多态性

接口概念是组合相关的属性和方法。尽管不能像对象那样实例化接口,但可以建立接口类型的变量,然后就可以在支持该接口的对象上,使用这个变量访问该接口提供的方法和属性。

假如,假定不使用基类Animal提供的EatFood()方法,而是把该方法放在接口IConsume上。Cow和Chicken类也支持这个接口,唯一的区别是它们必须提供EatFood()方法的执行代码(因为接口不包含执行代码),接着就可以使用下述代码访问该方法了:

Cow myCow = new Cow();

Chicken myChicken = new Chicken();

IConsume consumeInterface;

consumeInterface = myCow;

consumeInterface.EatFood();

consumeInterface = myChicken;

consumeInterface.EatFood;

这就提供了以相同方式访问多个对象的方式且不依赖于一个公共的基类。

注意,派生类会继承其基类支持的接口。有共同基类的类不一定有共同的接口,反之亦然。

8.2.4 对象之间的关系

包含关系:一个类包含另一个类。这类似于继承关系,但包含类可以控制对被包含类的成员的访问,甚至在使用被包含类的成员进行其他处理。

集合关系:一个类用作另一个类的多个实例的容器。这类似于对象数组,但集合有其他功能,包括索引、排序、重新设置大小等。

1.包含关系

把某个类声明为成员字段,就可以实现包含关系。这个成员字段可以是公共字段,此时与继承关系一样,容器对象的用户可以访问它的方法和属性,但不能像继承关系那样,通过派生类访问类的内部代码。也就是说,用户只能访问被包含类的公共属性和方法。

另外,可以让被包含的成员对象变成私有成员。如果这么做,用户就不能直接访问其成员,即使这些成员是公共的,也不能访问。但可以使用包含类的成员(间接)访问这些私有成员。在访问被包含类的成员前,可以在包含类的成员上进行其他处理。

例如,Cow类包含一个Udder类,它有一个公共方法Milk。Cow对象可以按照要求调用这个方法,作为其SuplyMilk()方法的一部分,但Cow对象的用户看不到这些细节。

在UML中,被包含类可以用关联线条来表示。对于简单的包含关系,可以用带有1的线条说明一对一的关系。

2.集合关系

使用数组存储相同类型的多个变量,这也适用于对象。例如:

Animal[] animals = new animal[5];

集合基本上是数组,集合以与其它对象相同的方式实现为类。它们通常以所存储的对象名称的复数形式来命名,例如类Animals包含Animal对象的一个集合。

数组与集合的主要区别是,集合通常执行额外的功能,例如Add()和Remove()方法可以添加和删除集合中的项。而且集合通常有一个Item属性 ,它根据对象的索引返回该对象。不仅如此,这个属性还允许以更复杂的访问方式实现。例如,如果设计一个Animals,Animal对象就可以根据其名称来访问。

8.2.5 运算符重载

可以把运算符用于从类实例化而来的对象,因为类可以包含运算符如何运算的指令。

例如,给Animal添加一个新属性Weight。接着使用下述代码比较家禽的重量:

If(cowA.Weight > cowB.Weight)

{

...

}

使用运算符重载,可以在代码隐式使用属性Weight的逻辑,例如下面的代码:

If(cowA > cowB)

{

...

}

也可以重载运算符,以相同的方式处理不同的类,其中一个(或两个)类定义包含达到这一目的的代码。

注意只能使用这种方式重载现有的C#运算符,不能创建新的运算符。

8.2.6 事件

对象可以激活事件,作为它们处理的一部分。事件是非常重要的,可以在代码的其他部分起作用,类似于异常(但功能更强大)。例如,可以在把Animal对象添加到Animals集合时,执行特定的代码,而这部分代码不是Animals类一部分,也不是调用Add()方法的代码的一部分。为此,需要给代码添加事件处理程序,这是一种特殊类型的函数,在事件发生时调用。还需要配置这个处理程序,以监听我们感兴趣的事件。

8.2.7 引用类型和值类型

在C#中,数据根据变量的类型以两种方式中的一种存储在一个变量中。变量的类型分为两种:引用类型和值类型。其区别如下:

  • 值类型在内存的一个地方(称为堆栈)存储它们自己和它们的内容。
  • 引用类型在内存的一个地方(称为堆)存储一个引用,而在另一个地方存储内容。

值类型和引用类型的一个主要区别是,值类型总是包含一个值,而引用类型可以是null,表示它们不包含值。但是,可以使用可空类型(这是泛型的一种形式)创建一个值类型,是值类型在这个方面的行为类似于引用类型(即可以为null)。

类就是引用类型。

结构类型和类的关键区别是,结构类型是值类型。

posted @ 2008-03-30 16:59  清香的工夫茶  阅读(907)  评论(1编辑  收藏  举报