抽象类
Abstract Classes
Control的每个子类都应自己实现DrawWindow()方法,但没有硬性的要求。要指定子类必须实现基类的方法,应该指明该方法是抽象的(abstract)。
抽象方法没有实现。它只创建派生类都要实现的方法名和签名。而且,使类的一个或多个方法为抽象的,也会有一个副效应:会使类也变成抽象的。
抽象类是派生类的基,但是实例化抽象类的对象却是非法的。一旦声明方法为抽象的,就不能创建该类的任何实例。
这样,如果将Control类中的DrawWindow()指定为abstract,就可以从Control派生,但不能创建任何Control对象。每个派生类都要实现DrawWindow()。如果派生类没有实现抽象方法,类就也是抽象的,也不能有实例。
将方法指定为abstract,是通过在方法定义前加上abstract 关键字实现的,如下所示:
abstract public void DrawWindow();
(因为方法没有实现,也就没有大括号,只有一个分号。)
如果一个或多个方法为抽象的,类定义也要标为abstract的,如下所示:
abstract public class Control
示例5-2演示了抽象类Control和抽象方法DrawWindow()的创建。
示例5-2:使用抽象类和方法
#region Using directives
using System;
using System.Collections.Generic;
using System.Text;
#endregion
namespace abstractmethods
{
using System;
abstract public class Control
{
protected int top;
protected int left;
// 构造方法有两个整数参数
// 以确定控制台上的位置
public Control( int top, int left )
{
this.top = top;
this.left = left;
}
// 模拟窗口绘制
// 注意:没有实现
abstract public void DrawWindow();
}
// ListBox 派生自 Control
public class ListBox : Control
{
private string listBoxContents; // 新的成员变量
// 构造方法添加一个参数
public ListBox(
int top,
int left,
string contents ):
base(top, left) // 调用基构造方法
{
listBoxContents = contents;
}
示例5-2:使用抽象类和方法(续例)
// 重定义版本实现
// 抽象方法
public override void DrawWindow()
{
Console.WriteLine( "Writing string to the listbox: {0}",
listBoxContents );
}
}
public class Button : Control
{
public Button(
int top,
int left ):
base(top, left)
{
}
// 实现抽象方法
public override void DrawWindow()
{
Console.WriteLine( "Drawing a button at {0}, {1}/n",
top, left );
}
}
public class Tester
{
static void Main()
{
Control[] winArray = new Control[3];
winArray[0] = new ListBox( 1, 2, "First List Box" );
winArray[1] = new ListBox( 3, 4, "Second List Box" );
winArray[2] = new Button( 5, 6 );
for ( int i = 0; i < 3; i++ )
{
winArray[i].DrawWindow();
}
}
}
}
示例5-2中,Control类被声明为抽象的,因此不能实例化。如果将第一个数组成员:
winArray[0] = new ListBox(1,2,"First List Box");
替换为以下代码:
winArray[0] = new Control(1,2);
则程序会出现如下错误:
无法创建抽象类或接口'Control'的实例
我们可以实例化ListBox和Button对象,因为这些类重定义了抽象方法,使类变成具体(concrete)而不是抽象的了。
抽象的局限
Limitations of Abstract
虽然将DrawWindow()指定为抽象的,会强制要求所有派生类实现此方法,但这是局限性很大的解决方案。如果从ListBox派生一个类(如DropDownListBox),就无法强制派生类必须实现DrawWindow()方法。
提示:C++程序员注意,C#中为Control.DrawWindow()提供实现是不可能的,因此不能利用公用的被派生类共享的DrawWindow()子例程。
最后,抽象类不仅是一种实现技巧,它还代表了一种抽象的理念,要为所有派生类创建一个“合同(contract)”。也就是说,抽象类描述了要实现该抽象的所有类的公共方法。
抽象类Control应该反映所有Control的共性,即使永远不用实例化抽象的Control本身。
抽象类的概念,顾名思义,应该实现各种Control的具体实例都具有的“Control”这个抽象的概念。比如浏览器窗口、图文框、按钮、列表框、下拉列表框,等等。抽象类确立了什么是Control这一概念,虽然,我们并没有真正创建一个“Control”。使用abstract的一种替换方式是定义一个接口,将在第八章讲述。
密封类
Sealed Class
与抽象相对的设计概念是密封(sealed)。抽象类是用来被派生,并为其子类提供遵循模板;而密封类则完全不允许被派生。关键字 sealed置于类声明之前将阻止进行派生。类被标记为sealed往往就是为了防止偶然继承。
提示:Java程序员注意了,C#中的密封类等价于Java中的final类。
如果示例5-2中的Control声明从abstract改为sealed(同样也要从DrawWindow()声明中删去abstract 关键字),程序就无法编译了。如果进行编连,编译器会返回如下错误信息:
'ListBox'无法从密封类'Control'继承
还有其他信息(例如不能在密封类中创建新的保护成员)。