本章旨在教会你:
● 定类一个类,在其中包含一系列相互关联的方法和数据项。
● 使用public和private关键字,控制类成员的可访问性
● 使用new关键字来创建对象并调用一个构造函数来初始化它
● 自己编写并调用构造函数
● 使用static关键字来创建可由同一个类的所有实例共享的方法和数据
通过本书第1部分的学习,你已经理解了如何声明变量,如何使用操作符来创建值,如何调用方法,以及如何编写实现一个方法时需要的许多语句。有了这些知识储备之后,就可以进入下个阶段的学习了。在本书第II部分,将学习如何将方法和数据合并到自己的类中。
Microsoft .NET Framework包含数量众多的类,前面已经用过了不少,例如Console和Exception类。类提供了对应用程序操纵的实体进行建模的一种便利的机制。实体可以代表一样具体的东西,例如一名客户;也可以代表比较抽象的东西,例如事务(业务)处理。任何系统在设计的时候,都需要首先确定哪些实体是重要的,然后执行分析,确定它们需要容纳什么信息,以及它们应该具有哪些功能。一个类容纳的信息存储在宇段(field)中,类要提供的功能则用方法(method)来实现。(译者注:在c#的术语中,字段是指类或结构中的对象或值。类和结构用字段来封装数据。欲知详情,请在MSDN中查找“字段,c#”这个索引项。)
通过学习第2部分的各章,可以完全掌握如何创建类。
7.1理解分类从英文上看,类(class)是分类(classification)的词根。设计一个类的过程就是对信息进行分类,将相关信息放到一个有意义的实体中的过程。任何人都会分类—并非只有程序员才会。例如,所有汽车都具有通用的行为(都能转向、停止、加速等)和通用的属性(都有方向盘、发动机等)。人们用“汽车”一词来泛指具有这些行为和属性的对象。只要所有人都认同一个词的意思,这个词就能很好地发挥作用。这样一来,人们就可以使用简练的形式来表达复杂而精确的意思。不会分类,很难想象人们如何思考或交流。 既然分类已在我们思考及交流的过程中根深蒂固,那么在写程序的时候,也很有必要尝试对个问题及其解决方案巾固有的概念进行分类,然后用一种编程语言对这些类进行建模,这正是包括Microsoft Visual C#在内的现代面向对象编程语言的宗旨。
7.2封装的目的定义一个类时,封装(encapsulation)是一个重要原则。它的中心思想是:使用一个类的程序不应该关心类的内部实际如何工作。程序只需创建类的一个实例,然后调用类的方法。只要那些方法能做到它们宣称能做的事情,程序就不关心它们具体是如何实现的。例如,当你调用Console.WriteLine方法时,肯定不会想到去了解Console类将数据输出到屏幕所涉及的复杂细节。一个类为了执行它的各个方法,可能需要维护各种内部状态信息,并且要在在内部采取各种行动。在使用类的程序面前,这些附加的状态信息和行动是隐藏的。因此,封装有时也称为信息隐藏(information hiding)。封装实际有以下两个目的。
● 将方法和数据合并到一个类中:换言之,为了支持分类。
● 控制对方法和数据的访问:换言之,为了控制类的使用。
7.3定义并使用类在C#中,要用一个class关键字来定义一个新的类,类的数据和方法位于类的主体中(在两个大括号之间)。以下是一个名为Circle的C#类,其中包含一个方法(用于计算圆的面积)和一个数据(圆的半径):
Class Circle
{
Double Area()
{
Return Math.PI*radius*radius;
}
int radius;
}
注意 Math类包含了用于执行数学计算的方法,另外还用一些字段来定义了数学常量。其中,Math.PI字段包含了值314159265358979323846,这是π的近似值。
在类的主体中,包含了普通的方法(如Area)和字段(如radius)。记住,在C#术语中,类中的变量称为字段(field)。第2章已经讲过如何声明变量,第3章已经讲过如何编写方法。换言之,这里实际上没有出现新的语法。
Circle类的用法类似于前面用到的其他类型。创建一个变量,使用Circle作为它的类型名称,然后使用一些有效的数据来初始化变量。下面是一个例子:
Circle c; //创建一个Circle变量
c=new Circle(); //初始化它
注意,这里使用了new关键字。以前,在初始化一个int或float变量时,是直接向它赋值,但是,对于类类型的变量,则不能像上面那样赋值。一个原因是C#没有提供将文字常量赋给一个变量的语法(一个等于42的Circle是什么意思?)。另一个原因则涉及“运行时”(runtime)对类类型的变量的内存进行分配与管理的方式,这方面的详情将在第8章讨论。就目前来说,你只需接受这样一个事实:new 关键字将新建类的一个实例—所谓“类的 一个实例”,一个更通俗的说法就是“对象”。
但是,我们可以直接把一个类的实例赋给同一类型的另一个变量,如下所示:
Circle c;
C= new Circle(),
Circle d
d=c;
然而,假如这样赋值,实际发生的事情或许并不是你想象的那样。第8章将解释具体原因。
重要提示:类和对象这两个术语不能混淆.类是一个类型的定义,对象则是该类型的一个实例,是在程序运行时才创建的。换言之,类是建筑蓝图,对象是按照这份蓝图采建造的房子。
7.4控制可访问性令人惊讶的是:.Circle类目前没有任何实际的用处。将方法和数据封装到一个类的内部之后,类就和外部世界划清了界线。类内定义的字段(如radius)和方法(如Area)可以被类内的其他方法看见,但外界无法看到它们。换言之,它们是类“私有”的。虽然能在一个程序中创建Circle对象,但不能访问它的radius字段,也不能调用它的Area方法。正因为此,该类目前并没有多大用处。然而,可以使用public和private关键字来修改字段或方法的定义,决定它们是否能从外部访问。
● 一个方法或字段假如只允许从类的内部访问,就说它是私有(private)的。为了声明一个private方法或字段,只需在它的声明之前添加private关键字。这个关键字实际是默认的,但作为一种良好的编程习惯,应该显式地将字段和方法声明为private,以避免混淆。
● 一个方法或字段假如既能从类的内部访问,也能从外部访问,就说它是公共(public)的。为了声明一个public方法或字段,只需在它的声明之前添加public关键字。下面是修改过的Circle类。这一次,Area方法被声明为一个pubic方法,而radius被声明为个private字段:
class Circle
{
public Double Area()
{
return Math.PI*radius*radius;
}
private int radius;
}
注意:c++程序员请注意,public或private关键字之后不要添加冒号,在每个字段和方法声明中,都必须重复输入public或private关键字。
注意:radius被声明为一个private字段:它将不能从类的外部访问。然而,radius可以从Circle类的内部访问。正因为此.Area方法才能访问radius字段。由于Area在Circle类的内部,所以Area的主体能访问radius。在像上面那样修改之后,Circle类的价值仍然有限,因为没有办法对radius字段进行初始化。为了解决这个问题,我们将使用一个构造函数。
提示:类中的字段自动初始化为o,false或者null,具体视类型而定.然而,作为一种良好的编程习惯,应该显式地初始化字段。
命名和可访问性NET Framework推荐了以下字段和方法命名约定,它们是以类成员的可访问性为出发点来考虑的。
● public的标识符应该以大写字母开头,例如,Area要以A开头(而不是a),因为它是public的.这是所谓的PascalCase命名法(因为它最早是在Pascal语言中使用的)。
● 非public的标识符(其中包括局部变量)应该以小写字母开头,例如,radius要以r开头(而不是R),因为它是private的。这是所谓的camelCase命名法。
上述规则仅有一个例外:类名应该以大写字母开头,而构造函数必须完全与类同名。所以,一个private构造函数也应该以大写字母开头。
重要提示:不要声明名称只是大小写有别的两个public类成员。否则,类就无法供其他不区分大小写的语言使用,例如Microsoft Visual Basic。;