目录
4.1 类的概述
类是一种活动的数据结构
4.2 程序和类:一个快速实例
4.3 声明类
4.4 类成员
4.4.1 字段
1.显示和隐式字段初始化
2. 声明多个字段
4.4.2 方法
4.5 创建变量和类的实例
4.6 为数据分配内存
合并这两个步骤
4.7 实例成员
4.8 访问修饰符
私有访问和共有访问
1.公有访问和私有访问图示
2.成员访问实例
4.9 从类的内部访问成员
4.10 从类的外部访问成员
4.11 综合应用
4.1 类的概述
在上一章中,我们看到C#提供了用户6种用户定义类型。其中最重要的,也是首先要阐述的是类。因为类在C#中是个很大的主题,关于它的讨论将会延伸到接下来的几章。
类是一种活动的数据结构
在面向对象的分析和设计产生之前,程序员们仅把程序当做指令的序列。那时的焦点主要放在指令的组合和优化上。随着面向对象的出现,焦点从优化指令转移到组织程序的数据和功能上来。程序的数据和功能被组织为逻辑上相关的数据项和函数的封装集合,并被称为类。
类是一个能存储数据并执行代码的数据结构。它包含数据成员和函数成员。
- 数据成员 它存储与类或类的实例相关的数据。数据成员通常模拟该类所表示的现实世界事物的特性。
- 函数成员 它执行代码。通常会模拟类所表示的现实世界事物的功能和操作。
一个C#类可以有任意数目的数据成员和函数成员。成员可以是9种可能的成员类型的任意组合。这些成员类型如表4-1所示。本章将会阐述字段和方法。
4.2 程序和类:一个快速实例
一个运行中的C#程序是一组相互作用的类型对象,他们中的大部分是类的实例。例如,假设有一个模拟扑克牌游戏的程序。当程序运行时,他有一个名为Dealer的类的实例,它的工作就是运行游戏。还有几个名为Player的类实例,他们代表游戏的玩家。
Dealer对象保存纸牌的当前状态和玩家数目等信息。它的动作包括洗牌和发牌。
Player类有很大不同。它保存玩家名称以及用于押注的钱的信息,并实现如分析玩家当前手上的牌和出牌这样的动作。运行中的程序如图4-1所示。类名显示在方框外面,实例名显示在方框内。
一个真正的程序无疑会包含除Dealer和Player之外的许多其他的类,还会包括像Card和Deck这样的类。每一个类都模拟某种扑克牌游戏中的事物。
4.3 声明类
或许你能猜到,虽然类型int、double和char由C4#定义,但像Dealer和Player这样的类不是由语言定义的。如果想在程序中使用他们,你必须自己定义,通过编写类的声明定义类。
类的声明定义类的特征和成员。它并不创建类的实例,但创建了用于创建实例的模板。类的声明提供下列内容:
- 类的名称;
- 类的成员;
- 类的特征。
下面是一个最简单的类声明语法示例。大括号包含了成员的声明,他们组成了类主体。类成员可以在类主体内部以任何顺序声明。这意味着成员的声明完全可以引用另一个在后面的类声明中才定义的成员。
4.4 类成员
字段和方法是最重要的类成员类型。字段是数据成员,方法是函数成员。
4.4.1 字段
字段是隶属于类的变量。
- 它可以是任何类型,无论是预定义类型还是用户定义类型。
- 和所有变量一样,字段用来保存数据,并具有如下特性;
- 他们可以被写入;
- 它们可以被读取。
声明一个字段最简单的语句如下:
1.显示和隐式字段初始化
因为字段是一种变量,所以字段初始化语句在语法上和上一章所描述的变量初始化语句相同。
- 字段初始化语句是字段声明的一部分,由一个等于号后面跟着一个求值表达式组成。
- 初始化值必须是编译时可确定的。
class Myclass
{
int F1 = 17; //字段初始值
}
- 如果没有初始化语句,字段的值会被编译器设置为默认值,默认值由字段类型决定。简单类型的默认值见表3-1(第三章)。可是总结起来,每种类型的默认值都是0,bool型是false,引用类型默认为null。
例如,下面的代码声明了4个字段,前两个字段被隐式初始化,另外两个字段被初始化语句显示初始化。
2. 声明多个字段
可以通过用逗号分隔名称的方式,在同一条语句中声明多个相同类型的字段,但是不能再一个声明中混合不同的类型。例如,可以把之前的4个字段声明结合成两条语句,并有相同的语义结果。
4.4.2 方法
方法是具有名称的可执行代码块,可以从程序的很多不同的地方执行,甚至从其他程序中执行。(还有一种没有名称的匿名方法,将在第13章讨论)
当方法被调用(call/invoke)时,他执行自己所含的代码,然后返回到调用它的代码并继续执行调用代码。有些方法返回一个值到他们被调用的位置。方法相当于C++中的成员函数。
声明方法最简单的语法包括以下组成部分。
- 返回类型 它声明了方法返回值的类型,如果一个方法不返回值,那么返回值被指定为viid。
- 名称 这是方法的名字。
- 参数列表 它至少由一对空的圆括号组成。如果有参数(参数将在下一章阐述),将被列在圆括号中间。
- 方法体 他由一对大括号组成,大括号内包含执行代码。
例如,下面的代码声明了一个类,带有一个名称为PrintNums的简单方法。从这个声明中可以看出下面几点关于PrintNums的情况:
- 他不返回值,因此返回类型指定为void;
- 他由空的参数列表
- 它的方法体有两行代码,第一行打印数字1,第二行打印数字2.
4.5 创建变量和类的实例
类的声明只是用于创建类的实例的蓝图。一旦类被声明,就可以创建类的实例。
- 类是引用类型,正如你从上一章学到的,这意味着它们要为数据引用和实际数据都申请内存。
- 数据的引用保存在一个类类型的变量中。所以,要创建类的实例,需要从声明一个类类型的变量开始。如果变量没有被初始化,它的值是未定义的。
图4-2阐明了如何定义保存引用的变量。左边顶端的代码是类Dealer的声明,下面是类Program的声明,它包含Main方法。Main声明了Dealer类型的变量TheDealer,因为变量没有初始化,它的值是未定义的,如图4-2的右边所示。
4.6 为数据分配内存
声明类类型的变量所分配的内存是用来保存引用的,而不是用来保存类对象实际数据的。要为实际数据分配内存,需要使用new运算符。
- new运算符为任意指定类型的实例分配并初始化内存。他依据类型不同从栈或堆里分配。
- 使用new运算符组成一个对象创建表达式,它的组成如下;
- 关键字new;
- 要分配内存的实例的类型名称;
- 成对的圆括号,可能包括参数或没有参数(后面会进一步讨论参数);
- 如果内存分配给一个引用类型,则对象创建表达式返回一个引用,指在堆中被分配并初始化的对象实例。
要分配和初始化用于保存类实例数据的内存,需要做的工作就是这些。下面是使用new运算符创建对象创建表达式,并把它的返回值赋给类变量的一个例子:
图4-3左边的代码展示了用于分配内存并创建类Dealer实例的new运算符,随后实例被赋值给类变量。右边的图展示了内存的结构。
合并这两个步骤
可以将这两步骤合并起来,用对象创建表达式来出初始化变量。
4.7 实例成员
类声明相当于蓝图,通过这个蓝图想创建多少个类的实例都可以。
- 实例成员 类的每个实例都是不同的实体,他们有自己的一组数据成员,不同于同一类的其他实例。因为这些数据成员都和类的实例相关,所以被称作实例成员。
- 静态成员 实例成员时默认类型,但也可以声明与类而不是实例相关的成员,称为静态成员,我们将会在第6章阐述。
下面代码是实例成员的示例,展示了由3个Player类实例的扑克牌程序。图4-4表明每个实例的Name字段都有不同的值。
4.8 访问修饰符
从类的内部,任何函数成员都可以使用成员的名称访问类中任意的其他成员。
访问修饰符是成员声明的可选部分,指明程序的其他部分如何访问成员。访问修饰符放在简单声明形式之前。下面是字段和方法声明的语法:
5种成员访问控制如下。本章将阐明前两种,其他的在第7章阐述。
- 私有的(private);
- 共有的(public);
- 受保护的(protected);
- 内部的(internal)
- 受保护内部的(protected internal)。
私有访问和共有访问
私有成员只能从声明它的类的内部访问,其他的类不能看见或访问它们。
- 私有成员是默认的访问级别,所以,如果一个成员在声明时不带访问修饰符,那他就是私有成员。
- 还可以使用private访问修饰符显示的将一个成员声明为私有。隐式地声明私有成员和显式地声明在语义上没有不同,两种形式是等价的。
例如,下面的两个声明都指定了private int 成员:
实例的公有成员可以被程序中的其他对象访问。必须使用public访问修饰符指定公有访问。
1.公有访问和私有访问图示
本文中的插图把类表示为标签框,如图4-5所示;
- 类成员被表示为类框中的小标签框;
- 私有成员被表示为完全封闭在它们的类框 中;
- 公有成员被表示为部分伸到它们的类框之外。
2.成员访问实例
类C1声明了公有和私有的字段和方法,图4-6阐明了类C1的成员的可见性。
4.9 从类的内部访问成员
如前所属,类的成员仅用其他类成员的名称就可以访问它们。
例如,下面的类声明了类的方法对字段和其他方法的访问。即使字段和两个方法被声明为private,类的所有成员还是都可以被类的任何方法(或者任何函数成员)访问。图4-7阐明了这段代码。
4.10 从类的外部访问成员
要从类的外部访问实例成员,必须包括变量名称和成员名称,中间用句点(.)分隔。这称为点运算符(dot-syntax notation),将会在以后更详细地讨论。
例如,下面代码的第二行展示了一个从类的外部访问方法的实例:
举个例子,下面的代码声明了两个类:DaysTemp和Program。
- DaysTemp内部的两个字段被声明为public,所以可以从类的外部访问它们。
- 方法Main是类Program的成员。他创建了一个变量和类DaysTemp的对象,并赋值给对象的字段。然后把它读取字段的值打印出来。
4.11 综合应用
下面的代码创建两个实例并把它们的引用保存在名称为t1和t2的变量中。图4-8阐明了内存中的t1和t2.这段代码示范了目前为止讨论的使用的3中行为:
- 声明一个类;
- 创建类的实例;
- 访问类的成员(也就是写入和读取字段)。