第三章 c# 面向对象初级编程

87
第第第 第第第 C# C# 第第第第第第第第 第第第第第第第第 第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第 第第第第 第第第第第第 。、 第第第第第第第第第第第第第第第第第第第 第第第第 第第第第第第 。、 第第第第第第第第第 第第第第第第第第 第第第第第第第第第 第第第第第第第第 C# C# 第第第第第第第第第第 第第第第第第第第第第 第第第第第第

Upload: agnes

Post on 22-Jan-2016

40 views

Category:

Documents


2 download

DESCRIPTION

第三章 C# 面向对象初级编程. 面向对象得程序设计越来越受到编程人员的喜爱。类和对象是面向对象程序设计中的重要概念。封装性、继承性和多态性是面向对象的特点,本章旨在全面说明 C# 编写面向对象程序设计的方法。. 3.1 面向对象的基本概念. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 第三章  C# 面向对象初级编程

第三章 第三章 C#C# 面向对象初级编程面向对象初级编程

面向对象得程序设计越来越受到编程人面向对象得程序设计越来越受到编程人员的喜爱。类和对象是面向对象程序设计员的喜爱。类和对象是面向对象程序设计中的重要概念。封装性、继承性和多态性中的重要概念。封装性、继承性和多态性是面向对象的特点,本章旨在全面说明是面向对象的特点,本章旨在全面说明 C#C#编写面向对象程序设计的方法。编写面向对象程序设计的方法。

Page 2: 第三章  C# 面向对象初级编程

3.1 3.1 面向对象的基本概念面向对象的基本概念 早期的程序设计方法多为面向过程的程序设计思想(早期的程序设计方法多为面向过程的程序设计思想( POPPOP ,, ProcProc

edure-Oriented Programmingedure-Oriented Programming ),在这种设计方法下,编程人员的),在这种设计方法下,编程人员的主要任务是把一个处理分解成若干个过程,然后编写这些过程。每个主要任务是把一个处理分解成若干个过程,然后编写这些过程。每个过程都基于某些特定的算法。对于过程都基于某些特定的算法。对于 CC 语言来说就是编写一个个函数,语言来说就是编写一个个函数,每个函数的数据和程序代码是分离的,当修改某段程序时,所有与之每个函数的数据和程序代码是分离的,当修改某段程序时,所有与之有关的部分都需要作相应的调整。随着问题规模的增大,程序变得容有关的部分都需要作相应的调整。随着问题规模的增大,程序变得容易出错,而且越来越难以管理。这种面向过程的程序设计语言有易出错,而且越来越难以管理。这种面向过程的程序设计语言有 CC 、、PascalPascal 、、 BasicBasic 等。等。

面向对象的程序设计(面向对象的程序设计( OOPOOP ,, Object-Oriented ProgramminObject-Oriented Programmingg )则是一种基于结构分析的、以数据为中心的程序设计方法。它的)则是一种基于结构分析的、以数据为中心的程序设计方法。它的主要思想是将数据及处理这些数据的操作都封装(主要思想是将数据及处理这些数据的操作都封装( EncapsulationEncapsulation ))到一个称为类(到一个称为类( ClassClass )的数据结构中。使用这个类时,只需要定义)的数据结构中。使用这个类时,只需要定义一个类的变量即可,这个变量叫做对象(一个类的变量即可,这个变量叫做对象( ObjectObject )。通过调用对象)。通过调用对象的数据成员完成对类的使用。这类编程思想较好地适应了现实世界中的数据成员完成对类的使用。这类编程思想较好地适应了现实世界中的问题,因而得以广泛应用。 的问题,因而得以广泛应用。

Page 3: 第三章  C# 面向对象初级编程

3.1.1 3.1.1 对象和类对象和类

在我们的日常生活中,对象是指可辨识的一种实体。例如汽车、在我们的日常生活中,对象是指可辨识的一种实体。例如汽车、房子、书、文档和支票等。为了进一步说明,可以把对象看作是用于房子、书、文档和支票等。为了进一步说明,可以把对象看作是用于在程序中表示的一个实体。因此对象可以包括把有生命的“对象”—在程序中表示的一个实体。因此对象可以包括把有生命的“对象”——人、员工、客户,以及更抽象的“对象”——公司、数据库和国家—人、员工、客户,以及更抽象的“对象”——公司、数据库和国家等。等。

面向对象程序设计(面向对象程序设计( OOPOOP )通过使用真实世界的对象实例概念,)通过使用真实世界的对象实例概念,改变了应用程序处理问题的模式。非面向对象的传统程序语言,必须改变了应用程序处理问题的模式。非面向对象的传统程序语言,必须针对特定问题开发应用程序,一旦所处理的问题脱离了原先考虑设计针对特定问题开发应用程序,一旦所处理的问题脱离了原先考虑设计的范围,就得扩充应用程序得功能以面对新产生的问题;而在这种情的范围,就得扩充应用程序得功能以面对新产生的问题;而在这种情形下,整个应用程序不是大幅改写就是必须重新开发。形下,整个应用程序不是大幅改写就是必须重新开发。 OOPOOP 程序设程序设计理论的出现,解决了这样的缺陷,其中关键就在于对象的使用。计理论的出现,解决了这样的缺陷,其中关键就在于对象的使用。

我们可以设计不同的对象,处理应用程序所要解决的各种问题,我们可以设计不同的对象,处理应用程序所要解决的各种问题,其中最大的好处在于应用程序可以轻易地针对新的问题进行扩充处理,其中最大的好处在于应用程序可以轻易地针对新的问题进行扩充处理,只要修改原有的对象或是加入新定义的对象,完全不用重新改写原有只要修改原有的对象或是加入新定义的对象,完全不用重新改写原有的应用程序。的应用程序。

Page 4: 第三章  C# 面向对象初级编程

OOPOOP 得对象概念,使得开发应用程序得复杂度与难得对象概念,使得开发应用程序得复杂度与难度,不会随着程序规模的扩大,变得难以处理与掌握。度,不会随着程序规模的扩大,变得难以处理与掌握。

类是一组具有相同数据结构和相同操作的对象的集合。类是一组具有相同数据结构和相同操作的对象的集合。类是一系列具有相同性质的对象的抽象,是对对象共同特类是一系列具有相同性质的对象的抽象,是对对象共同特征的描述。比如每一辆汽车是一个对象的话,所有的汽车征的描述。比如每一辆汽车是一个对象的话,所有的汽车可以作为一个模板,我们就定义汽车这个类。可以作为一个模板,我们就定义汽车这个类。

在一个类中,每个对象都是类的实例,可以使用类中在一个类中,每个对象都是类的实例,可以使用类中提供的方法。从类定义中产生对象,必须有建立实例的操提供的方法。从类定义中产生对象,必须有建立实例的操作,作, C++C++和和 C#C# 中的中的 newnew操作符可用于建立一个类的实操作符可用于建立一个类的实例,例, C#C# 为我们提供的方法则更加安全。为我们提供的方法则更加安全。

Page 5: 第三章  C# 面向对象初级编程

3.1.2 3.1.2 继承继承继承是使用己存在的定义作为基础建立新定义的技术。继承是使用己存在的定义作为基础建立新定义的技术。

新类的定义可以是即存类所声明的数据和新类所增加的声新类的定义可以是即存类所声明的数据和新类所增加的声明组合。新类复用即存的定义,而不要求修改即存类。即明组合。新类复用即存的定义,而不要求修改即存类。即存类可以作为基类来引用,而新类可以作为派生类来引用。存类可以作为基类来引用,而新类可以作为派生类来引用。这种复用技术大大降低了软件的开发费用。这种复用技术大大降低了软件的开发费用。

例如,汽车作为一个类己经存在,作为具有自身特征例如,汽车作为一个类己经存在,作为具有自身特征的卡车就可以从汽车类中继承。它同汽车一样,具有颜色、的卡车就可以从汽车类中继承。它同汽车一样,具有颜色、重量这些特征,可以行驶和鸣笛。它还具有一般汽车不一重量这些特征,可以行驶和鸣笛。它还具有一般汽车不一定具有的特征,比如可以载货等。定具有的特征,比如可以载货等。

Page 6: 第三章  C# 面向对象初级编程

3.1.3 3.1.3 封装封装

一般来说,程序员都力求软件工程系统的高一般来说,程序员都力求软件工程系统的高集成性。一个具有高集成性的软件系统包含着各集成性。一个具有高集成性的软件系统包含着各种执行独立任务的成分,而每一个独立任务都是种执行独立任务的成分,而每一个独立任务都是整个系统的重要组成部分。相反,如果一个软件整个系统的重要组成部分。相反,如果一个软件系统的集成性差,那么系统所包含的各种成分由系统的集成性差,那么系统所包含的各种成分由于没有很好的被定义而往往会容易发生冲突。于没有很好的被定义而往往会容易发生冲突。

封装可以将对象相关的信息集中存放在一个封装可以将对象相关的信息集中存放在一个独立的单元中,因此,用一个标识符就可以访问独立的单元中,因此,用一个标识符就可以访问对象,还可以把整个对象作为一个变量参数传送对象,还可以把整个对象作为一个变量参数传送给函数。给函数。

Page 7: 第三章  C# 面向对象初级编程

3.1.4 3.1.4 多态多态

多态性是指用一个名字定义不同的函数,这多态性是指用一个名字定义不同的函数,这函数执行不同但又类似的操作,从而实现“一个函数执行不同但又类似的操作,从而实现“一个接口,多种方法”。接口,多种方法”。

多态性的实现与静态联编、动态联编有关。多态性的实现与静态联编、动态联编有关。静态联编支持的多态性称为编译时的多态性,也静态联编支持的多态性称为编译时的多态性,也称静态多态性,它是通过函数重载和运算符重载称静态多态性,它是通过函数重载和运算符重载实现的。动态联编支持的多态性称为运行时的多实现的。动态联编支持的多态性称为运行时的多态性,也称动态多态性,它是通过继承和虚函数态性,也称动态多态性,它是通过继承和虚函数实现的。实现的。

Page 8: 第三章  C# 面向对象初级编程

3.2 C#3.2 C# 中的类与对象中的类与对象3.2.1 3.2.1 在在 C#C# 中定义类中定义类类的声明格式如下:类的声明格式如下:[[类修饰符类修饰符 ] class ] class 类名 类名 [[ :基类名:基类名 ]]{{ 类的成员类的成员 ;;}}类的修饰符可以是以下几种之一或者是它们类的修饰符可以是以下几种之一或者是它们

的组合(在类的声明中同一修饰符不允许出现多的组合(在类的声明中同一修饰符不允许出现多次):次):

Page 9: 第三章  C# 面向对象初级编程

◆◆new——new——新建类,仅允许在嵌套类声明时使用,表新建类,仅允许在嵌套类声明时使用,表明类中隐藏了由基类中继承而来的、与基类中同名的成员。明类中隐藏了由基类中继承而来的、与基类中同名的成员。◆◆public——public—— 公有类,表示不限制对该类的访问。公有类,表示不限制对该类的访问。◆◆protectedprotected 一—保护类,表示只能从所在类和所在类一—保护类,表示只能从所在类和所在类

派生的子类进行访问。派生的子类进行访问。◆◆internal——internal—— 内部类,只有其所在类才能访问。内部类,只有其所在类才能访问。◆◆private——private——私有类,只有对包私有类,只有对包 .Net.Net 中的应用程序或中的应用程序或

库才能访问。库才能访问。◆◆abstract——abstract—— 抽象类,不允许建立类的实例。抽象类,不允许建立类的实例。◆◆sealed——sealed—— 密封类,不允许被继承。密封类,不允许被继承。以上类修饰符可以两个或多个。以上类修饰符可以两个或多个。

Page 10: 第三章  C# 面向对象初级编程

使用使用 newnew关键字可以建立类的一个实例,比如下面的代码:关键字可以建立类的一个实例,比如下面的代码:class Aclass A{{}}class Bclass B{{ void Funvoid Fun {{ A a;A a; a=new Aa=new A()() ;; }}}}在类在类 BB 的方法的方法 FunFun 中创建了一个类中创建了一个类 AA的实例。的实例。我们使用如下代码表示类我们使用如下代码表示类 BB从类从类 AA中继承:中继承:class Aclass A{ }{ }class B: A{ }class B: A{ }有关有关 C#C# 中的继承机制我们放在后面的章节中进行详细讨论,在这中的继承机制我们放在后面的章节中进行详细讨论,在这

里要事先声明的一点是:里要事先声明的一点是: C#C# 中的类只支持单继承。中的类只支持单继承。

Page 11: 第三章  C# 面向对象初级编程

3.2.2 3.2.2 访问修饰符访问修饰符类的成员有以下类型:类的成员有以下类型:◆◆ 成员常量,代表与类相关的常量值。成员常量,代表与类相关的常量值。◆◆ 域,即类中的变量。域,即类中的变量。◆◆ 成员方法,完成类中各种计算或功能的操作。成员方法,完成类中各种计算或功能的操作。◆◆ 属性,用于定义类中的值,并对它们提供读、写操作。属性,用于定义类中的值,并对它们提供读、写操作。◆◆ 事件,用于说明发生了什么事情。事件,用于说明发生了什么事情。◆◆ 索引指示器,允许编程人员在访问数组时,通过索引指示器访索引指示器,允许编程人员在访问数组时,通过索引指示器访

问类的多个实例。问类的多个实例。◆◆ 操作符,定义类中特有的操作。操作符,定义类中特有的操作。◆◆ 构造函数,在类被实例化时首先执行的函数,主要是完成对象构造函数,在类被实例化时首先执行的函数,主要是完成对象

初始化操作。初始化操作。◆◆ 析构函数,在对象被销毁之前最后执行的函数,主要是完成对析构函数,在对象被销毁之前最后执行的函数,主要是完成对

象结束时的收尾操作。象结束时的收尾操作。

Page 12: 第三章  C# 面向对象初级编程

包含有可执行代码的成员被认为是类中的函数成员,这些函数成包含有可执行代码的成员被认为是类中的函数成员,这些函数成员有方法、属性、索引指示器、操作符、构造函数和析构函数。员有方法、属性、索引指示器、操作符、构造函数和析构函数。

在编写程序时,我们可以对类的成员使用不同的访问修饰符,从在编写程序时,我们可以对类的成员使用不同的访问修饰符,从而定义它们的访问级别。而定义它们的访问级别。

(( 11 )公有成员)公有成员C#C# 中的公有成员提供了类的外部界面,允许类的使用者从内部中的公有成员提供了类的外部界面,允许类的使用者从内部

或外部直接进行访问。公有成员的修饰符为或外部直接进行访问。公有成员的修饰符为 publicpublic ,这是限制最少,这是限制最少的一种访问方式。它的优先是使用灵活,缺点是外界可能会破坏对象的一种访问方式。它的优先是使用灵活,缺点是外界可能会破坏对象成员值得合理性。成员值得合理性。

(( 22 ) 私有成员) 私有成员C#C# 中的私有成员仅限于类中的成员可以访问,从类的外部访问中的私有成员仅限于类中的成员可以访问,从类的外部访问

私有成员是不合法的。如果在声明中没有出现成员的访问修饰符,按私有成员是不合法的。如果在声明中没有出现成员的访问修饰符,按照默认方式成员为私有的。私有成员的修饰符为照默认方式成员为私有的。私有成员的修饰符为 privateprivate 。。

(( 33 )保护成员)保护成员为了方便派生类的访问,又希望成员对于外界是隐藏的,这时可为了方便派生类的访问,又希望成员对于外界是隐藏的,这时可

以使用以使用 protectedprotected 修饰符,声明成员为保护成员。修饰符,声明成员为保护成员。(( 44 )内部成员)内部成员使用使用 internalinternal 修饰符的类的成员是一种特殊的成员。这种成员修饰符的类的成员是一种特殊的成员。这种成员

对于同一包中的应用程序或库是透明的,而在包对于同一包中的应用程序或库是透明的,而在包 .Net.Net 之外是禁止访之外是禁止访问的。问的。

Page 13: 第三章  C# 面向对象初级编程

使用下面的例子说明一下类的成员的访问修饰符的用法。使用下面的例子说明一下类的成员的访问修饰符的用法。程序清单:程序清单:class ClassAclass ClassA{{ public int a;public int a; private int b;private int b; protected int c;protected int c; public void SetApublic void SetA()() {{ a=1;//a=1;// 正确,允许访问类自身公有成员正确,允许访问类自身公有成员 b=2;//b=2;// 正确,允许访问类自身私有成员正确,允许访问类自身私有成员 c=3;//c=3;// 正确,允许访问类自身保护成员正确,允许访问类自身保护成员 }}}}class ClassB:Aclass ClassB:A{{ public void SetB()public void SetB() {{

Page 14: 第三章  C# 面向对象初级编程

ClassA BaseA=new ClassA();ClassA BaseA=new ClassA(); BaseA.a=11;//BaseA.a=11;// 正确,允许访问基类公有成员正确,允许访问基类公有成员 BaseA.b=22;//BaseA.b=22;//错误,不允许访问基类私有成员错误,不允许访问基类私有成员 BaseA.c=33;//BaseA.c=33;// 正确,允许访问基类保护成员正确,允许访问基类保护成员 }}}}

class ClassCclass ClassC{{ public void AetB()public void AetB() {{ ClassA BaseA=new ClassA();ClassA BaseA=new ClassA(); BaseA.a=111;//BaseA.a=111;// 正确,允许访问类的其他公有成员正确,允许访问类的其他公有成员 BaseA.b=222;//BaseA.b=222;//错误,不允许访问类的其他私有成员错误,不允许访问类的其他私有成员 BaseA.c=333;//BaseA.c=333;//错误,不允许访问类的其他保护成员错误,不允许访问类的其他保护成员 }}}}

Page 15: 第三章  C# 面向对象初级编程

(( 55 ) ) thisthis保留字保留字保留字保留字 thisthis仅限于在构造函数、类的方法和类的实例中使用,仅限于在构造函数、类的方法和类的实例中使用,

它有以下含义:它有以下含义:◆◆ 在类的构造函数中出现的在类的构造函数中出现的 thisthis 作为一个值类型,它表示对正作为一个值类型,它表示对正

在构造的对象本身的引用。在构造的对象本身的引用。◆◆ 在类的方法中出现的在类的方法中出现的 thisthis 作为一个值类型,它表示对调用该作为一个值类型,它表示对调用该

方法的对象的引用。方法的对象的引用。◆◆ 在结构的构造函数中出现的在结构的构造函数中出现的 thisthis 作为一个变量类型,它表示作为一个变量类型,它表示

对正在构造的结构的引用。对正在构造的结构的引用。◆◆ 在结构的方法中出现的在结构的方法中出现的 thisthis 作为一个变量类型,它表示对调作为一个变量类型,它表示对调

用该方法的结构的引用。用该方法的结构的引用。除此以外,在其已地方使用除此以外,在其已地方使用 thisthis保留字都是不合法的。保留字都是不合法的。

Page 16: 第三章  C# 面向对象初级编程

案例案例:: thisthis保留字的使用保留字的使用目标目标:学习保留字:学习保留字 thisthis 的使用方法的使用方法

步骤步骤::11 、启动、启动 VS.NETVS.NET,新建一个控制台应用程序,名称填写为“,新建一个控制台应用程序,名称填写为“ ThisTesThisTes

t”t” ,位置设置为“,位置设置为“ c:\CSharpSamples\chp3c:\CSharpSamples\chp3 。。22 、在代码设计窗口中编辑、在代码设计窗口中编辑 Class1.csClass1.cs 。在其中的代码如下:。在其中的代码如下:using System;using System;namespace thistestnamespace thistest{{

class Class1class Class1{{

public int x;public int x;public void aaa()public void aaa(){{

Page 17: 第三章  C# 面向对象初级编程

x=5;x=5;Console.WriteLine("The value of x is: {0}",Console.WriteLine("The value of x is: {0}",

x);x);Console.WriteLine("The value of this. is: Console.WriteLine("The value of this. is:

{0}", this.x);{0}", this.x);}}static void Main(string[] args)static void Main(string[] args){{

Class1 bb=new Class1();Class1 bb=new Class1();bb.aaa();bb.aaa();

}}}}

}}

Page 18: 第三章  C# 面向对象初级编程

55、按、按 Ctrl + F5Ctrl + F5编译并运行该程序,效果如图编译并运行该程序,效果如图 3-13-1所示。所示。图 图 3-1 3-1 程序运行结果程序运行结果

实际上,在实际上,在 C#C#内部,内部, thisthis被定义为一个常量。因此,被定义为一个常量。因此,使用使用 this++this++,, this--this-- 这样的语句都是不合法的。但是,这样的语句都是不合法的。但是, tthishis 可以作为返回值来使用。可以作为返回值来使用。

图 3-1 程序运行结果

Page 19: 第三章  C# 面向对象初级编程

3.2.3 3.2.3 实例化对象与构造函数实例化对象与构造函数

若将类中的某个成员声明为若将类中的某个成员声明为 staticstatic 该成该成员称为静态成员。类中的成员要么是静态,员称为静态成员。类中的成员要么是静态,要么是非静态的。一般说来,静态成员是要么是非静态的。一般说来,静态成员是属于类所有的,非静态成员则属于类的实属于类所有的,非静态成员则属于类的实例化对象。例化对象。

Page 20: 第三章  C# 面向对象初级编程

以下示例代码演示了如何声明静态和非静态成员。以下示例代码演示了如何声明静态和非静态成员。程序清单:程序清单:class Testclass Test

{{int x;int x;static int y;static int y;void F()void F()

{{ x=1;// x=1;// 正确,等价于正确,等价于 this.x=1this.x=1 y=1;// y=1;// 正确,等价于正确,等价于 Test.y=1Test.y=1

}}static void G()static void G(){{

x=1;//x=1;//错误,不能访问错误,不能访问 this.xthis.xy=1;//y=1;// 正确,等价于正确,等价于 Test.y=1Test.y=1

}}

Page 21: 第三章  C# 面向对象初级编程

static void Main(string[] args)static void Main(string[] args){{

Test.t=new Test();Test.t=new Test();t.x=1;//t.x=1;// 正确正确t.y=1;//t.y=1;//错误,不能在类的实例中访错误,不能在类的实例中访

问静态成员问静态成员Test.x=1;//Test.x=1;//错误,不能按类访问非错误,不能按类访问非

静态成员静态成员Test.y=1;//Test.y=1;// 正确正确

}}}}

Page 22: 第三章  C# 面向对象初级编程

类的非静态成员属于类的实例所有,每创建一个类的类的非静态成员属于类的实例所有,每创建一个类的实例,都在内存中为非静态成员开辟了一块区域。而类的实例,都在内存中为非静态成员开辟了一块区域。而类的静态成员属于类所有,为这个类的所有实例所共享。无论静态成员属于类所有,为这个类的所有实例所共享。无论这个类创建了多少个副本,一个静态成员在内存中只占有这个类创建了多少个副本,一个静态成员在内存中只占有一块区域。一块区域。

构造函数用于执行类的实例的初始化。每个类都有构构造函数用于执行类的实例的初始化。每个类都有构造函数,即使我们没有声明它,编译器也会自动地为我们造函数,即使我们没有声明它,编译器也会自动地为我们提供一个默认的构造函数。在访问一个类的时候,系统将提供一个默认的构造函数。在访问一个类的时候,系统将最先执行构造函数中的语句。实际上,任何构造函数的执最先执行构造函数中的语句。实际上,任何构造函数的执行都隐式地调用了系统提供默认的构造函数行都隐式地调用了系统提供默认的构造函数 basebase ()。()。

Page 23: 第三章  C# 面向对象初级编程

如果我们在类中声明了如下的构造函数,如果我们在类中声明了如下的构造函数,C(…){…}C(…){…}它等价于:它等价于:C(…):base(){…}C(…):base(){…}使用构造函数请注意以下几个问题:使用构造函数请注意以下几个问题:◆◆一个类的构造函数通常与类名相同。一个类的构造函数通常与类名相同。◆◆构造函数不声明返回类型。构造函数不声明返回类型。一般地,构造函数总是一般地,构造函数总是 publicpublic 类型的。如果类型的。如果

是是 privateprivate 类型的,表明类不能被实例化,这通类型的,表明类不能被实例化,这通常用于只含有静态成员的类。常用于只含有静态成员的类。

Page 24: 第三章  C# 面向对象初级编程

下面的例子示范了构造函数的使用下面的例子示范了构造函数的使用 :: class Aclass A {{ int x=0,y=0, count;int x=0,y=0, count; public A()public A() {{ count=0;count=0; }} public A(int vx,int vy)public A(int vx,int vy) {{ x=vx;x=vx; y=vy;y=vy; }} }}

Page 25: 第三章  C# 面向对象初级编程

构造函数的名字不能随便起,必须让编译器认构造函数的名字不能随便起,必须让编译器认得出才可以被自动执行。它的命名方法既简单又得出才可以被自动执行。它的命名方法既简单又合理:让构造函数与类同名。除了名字外,构造合理:让构造函数与类同名。除了名字外,构造函数的另一个特别之处是没有返回值类型,这与函数的另一个特别之处是没有返回值类型,这与返回值类型为返回值类型为 voidvoid 的函数不同。如果它有返回值的函数不同。如果它有返回值类型,那么编译器将不知所措。在你可以访问一类型,那么编译器将不知所措。在你可以访问一个类的方法、属性或任何其它东西之前, 第一条个类的方法、属性或任何其它东西之前, 第一条执行的语句是包含有相应类的构造函数。甚至你执行的语句是包含有相应类的构造函数。甚至你自己不写一个构造函数,也会有一个缺省构造函自己不写一个构造函数,也会有一个缺省构造函数提供给你。数提供给你。

Page 26: 第三章  C# 面向对象初级编程

class TestClassclass TestClass{{ public TestClass(): base() {} // public TestClass(): base() {} // 由由 CLRCLR提供提供}}下面列举了几种类型的构造函数。下面列举了几种类型的构造函数。11)缺省构造函数)缺省构造函数class TestClassclass TestClass{{ public TestClass(): base() {} public TestClass(): base() {} }}上面已介绍,它由系统(上面已介绍,它由系统( CLRCLR )提供。)提供。

Page 27: 第三章  C# 面向对象初级编程

22)实例构造函数)实例构造函数实例构造函数是实现对类中实例进行初始化的方法成员。如:实例构造函数是实现对类中实例进行初始化的方法成员。如: using System;using System; class Pointclass Point { public double x, y;{ public double x, y; public Point() public Point() { this.x = 0;{ this.x = 0; this.y = 0;this.y = 0; }} public Point(double x, double y)public Point(double x, double y) { this.x = x;{ this.x = x; this.y = y; this.y = y; }} …… …… }}

Page 28: 第三章  C# 面向对象初级编程

class Testclass Test{{ static void Main() static void Main() {{ Point a = new Point();Point a = new Point(); Point b = new Point(3, 4); // Point b = new Point(3, 4); // 用构造函数初始化对象用构造函数初始化对象 …… …… }}}}

Page 29: 第三章  C# 面向对象初级编程

声明了一个类声明了一个类 PointPoint ,它提供了两个构造函数。,它提供了两个构造函数。它们是重载的。一个是没有参数的它们是重载的。一个是没有参数的 PointPoint 构造函构造函数和一个是有两个数和一个是有两个 doubledouble参数的参数的 PointPoint 构造函构造函数。如果类中没有提供这些构造函数,那么会数。如果类中没有提供这些构造函数,那么会 CLCLRR会自动提供一个缺省构造函数的。但一旦类中会自动提供一个缺省构造函数的。但一旦类中提供了自定义的构造函数,如提供了自定义的构造函数,如 Point()Point()和和 Point(dPoint(double x, double y)ouble x, double y),则缺省构造函数将不会被提,则缺省构造函数将不会被提供,这一点要注意。供,这一点要注意。

Page 30: 第三章  C# 面向对象初级编程

33)静态构造函数)静态构造函数静态构造函数是实现对一个类进行初始化的方法成员。它一般用静态构造函数是实现对一个类进行初始化的方法成员。它一般用

于对静态数据的初始化。静态构造函数不能有参数,不能有修饰符而于对静态数据的初始化。静态构造函数不能有参数,不能有修饰符而且不能被调用,当类被加载时,类的静态构造函数自动被调用。如:且不能被调用,当类被加载时,类的静态构造函数自动被调用。如:

using System.Data;using System.Data;class Employeeclass Employee{{ private static DataSet ds;private static DataSet ds; static Employee()static Employee() {{ ds = new DataSet(...);ds = new DataSet(...); }} … …......}}

Page 31: 第三章  C# 面向对象初级编程

声明了一个有静态构造函数的类声明了一个有静态构造函数的类 EmployeeEmployee 。。注意静态构造函数只能对静态数据成员进行初始注意静态构造函数只能对静态数据成员进行初始化,而不能对非静态数据成员进行初始化。但是,化,而不能对非静态数据成员进行初始化。但是,非静态构造函数既可以对静态数据成员赋值,也非静态构造函数既可以对静态数据成员赋值,也可以对非静态数据成员进行初始化。可以对非静态数据成员进行初始化。

如果类仅包含静态成员,你可以创建一个如果类仅包含静态成员,你可以创建一个 privprivateate 的构造函数:的构造函数: private TestClass() {…}private TestClass() {…},但,但是是 privateprivate 意味着从类的外面不可能访问该构造意味着从类的外面不可能访问该构造函数。所以,它不能被调用,且没有对象可以被函数。所以,它不能被调用,且没有对象可以被该类定义实例化。该类定义实例化。

Page 32: 第三章  C# 面向对象初级编程

以上是几种类型构造函数的简单运用,下面以上是几种类型构造函数的简单运用,下面将重点介绍一下在类的层次结构中(即继承结构将重点介绍一下在类的层次结构中(即继承结构中)基类和派生类的构造函数的使用方式。派生中)基类和派生类的构造函数的使用方式。派生类对象的初始化由基类和派生类共同完成:基类类对象的初始化由基类和派生类共同完成:基类的成员由基类的构造函数初始化,派生类的成员的成员由基类的构造函数初始化,派生类的成员由派生类的构造函数初始化。由派生类的构造函数初始化。

当创建派生类的对象时,系统将会调用基类当创建派生类的对象时,系统将会调用基类的构造函数和派生类的构造函数,构造函数的执的构造函数和派生类的构造函数,构造函数的执行次序是:先执行基类的构造函数,再执行派生行次序是:先执行基类的构造函数,再执行派生类的构造函数。如果派生类又有对象成员,则,类的构造函数。如果派生类又有对象成员,则,先执行基类的构造函数,再执行成员对象类的构先执行基类的构造函数,再执行成员对象类的构造函数,最后执行派生类的构造函数。造函数,最后执行派生类的构造函数。

Page 33: 第三章  C# 面向对象初级编程

至于执行基类的什么构造函数,缺省情况下是执行基类的无参构造函数,如至于执行基类的什么构造函数,缺省情况下是执行基类的无参构造函数,如果要执行基类的有参构造函数,则必须在派生类构造函数的成员初始化表中指出。果要执行基类的有参构造函数,则必须在派生类构造函数的成员初始化表中指出。如:如:

class Aclass A{{ private int x;private int x; public A( ) { x = 0; }public A( ) { x = 0; } public A( int i ) { x = i; }public A( int i ) { x = i; }}}class B : Aclass B : A{{ private int y;private int y; public B( ) { y = 0; }public B( ) { y = 0; } public B( int i ) { y = i; }public B( int i ) { y = i; } public B( int i, int j ):A(i) { y = j; }public B( int i, int j ):A(i) { y = j; }}}

Page 34: 第三章  C# 面向对象初级编程

B b1 = new B(); //B b1 = new B(); // 执行基类执行基类 AA的构造函数的构造函数 A()A() ,再执,再执行派生类的构造函数行派生类的构造函数 B()B()

B b2 = new B(1); //B b2 = new B(1); // 执行基类执行基类 AA的构造函数的构造函数 A()A() ,再,再执行派生类的构造函数执行派生类的构造函数 B(int)B(int)

B b3 = new B(0,1); //B b3 = new B(0,1); // 执行执行基类执行执行基类 AA的构造函数的构造函数 A(iA(int)nt),再执行派生类的构造函数,再执行派生类的构造函数 B(int,int) B(int,int)

在这里构造函数的执行次序是一定要分析清楚的。另在这里构造函数的执行次序是一定要分析清楚的。另外,如果基类外,如果基类 AA中没有提供无参构造函数中没有提供无参构造函数 public A( ) { x = public A( ) { x = 0; }0; },则在派生类的所有构造函数成员初始化表中必须指,则在派生类的所有构造函数成员初始化表中必须指出基类出基类 AA的有参构造函数的有参构造函数 A(i)A(i),如下所示:,如下所示:

Page 35: 第三章  C# 面向对象初级编程

class Aclass A{{ private int x;private int x; public A( int i ) { x = i; }public A( int i ) { x = i; }}}class B : Aclass B : A{{ private int y;private int y; public B():A(i) { y = 0; }public B():A(i) { y = 0; } public B(int i):A(i) { y = i; }public B(int i):A(i) { y = i; } public B(int i, int j):A(i) { y = j; }public B(int i, int j):A(i) { y = j; }}}

Page 36: 第三章  C# 面向对象初级编程

3.2.4 3.2.4 方法重载方法重载

方法是类中用于执行计算或其它行为的成员。方法是类中用于执行计算或其它行为的成员。我们看一下方法的声明格式:我们看一下方法的声明格式:

方法修饰符 返回类型 方法名(方法参数列方法修饰符 返回类型 方法名(方法参数列表)表)

{{ 方法实现部分;方法实现部分; }}

Page 37: 第三章  C# 面向对象初级编程

11、修饰符、修饰符方法的修饰符可以是:方法的修饰符可以是: newnew、、 publicpublic 、、 protectedprotected 、、

internalinternal 、、 privateprivate 、、 staticstatic 、、 virtualvirtual 、、 sealedsealed 、、 overrioverridede 、、 abstractabstract 和和 externextern几种。几种。

如果修饰符为如果修饰符为 staticstatic 则表明这个方法只能访问类中的则表明这个方法只能访问类中的静态成员,没有修饰符静态成员,没有修饰符 staticstatic 的方法可以访问类中任意成的方法可以访问类中任意成员。员。

如果修饰符为如果修饰符为 virtualvirtual ,则称这个方法为虚方法,反之,则称这个方法为虚方法,反之称为非虚方法。对于非虚方法,无论是被用此类定义的对称为非虚方法。对于非虚方法,无论是被用此类定义的对象调用,还是被这个类的派生类定义的对象调用,方法的象调用,还是被这个类的派生类定义的对象调用,方法的执行方式不变。对于虚方法,它的执行方式可以被派生类执行方式不变。对于虚方法,它的执行方式可以被派生类改变,这种改变是通过重载实现的。改变,这种改变是通过重载实现的。

如果修饰符为如果修饰符为 externextern ,则表示这个方法是外部方法。,则表示这个方法是外部方法。

Page 38: 第三章  C# 面向对象初级编程

22、返回值、返回值方法的返回值的类型可以是合法的方法的返回值的类型可以是合法的 C#C# 的数的数

据类型。据类型。 C#C# 在方法的执行部分通过在方法的执行部分通过 returnreturn 语句语句得到返回值。得到返回值。

Page 39: 第三章  C# 面向对象初级编程

案例案例:求最大值、最小值。:求最大值、最小值。目标目标:掌握方法的格式使用:掌握方法的格式使用

步骤步骤::11、启动、启动 VS.NETVS.NET,新建一个控制台应用程序,名称填写为,新建一个控制台应用程序,名称填写为

““ MaxandMinTest”MaxandMinTest” ,位置设置为“,位置设置为“ c:\CSharpSamples\cc:\CSharpSamples\chp3”hp3” 。。22、在代码设计窗口中编辑、在代码设计窗口中编辑 Class1.csClass1.cs 。在其中的代码如下:。在其中的代码如下:using System;using System;namespace MaxandMinTestnamespace MaxandMinTest{ class Test{ class Test{ public static int max(int x ,int y){ public static int max(int x ,int y)

{ if(x>y){ if(x>y)return x;return x;

elseelsereturn y;return y;

}}

Page 40: 第三章  C# 面向对象初级编程

public static void WriteMin(int x,int y)public static void WriteMin(int x,int y){{

int temp=x;int temp=x;if(x>y)if(x>y)

temp=y;temp=y;Console.WriteLine("the min of {0} and {1} isConsole.WriteLine("the min of {0} and {1} is::

{2}{2}。。 ",x,y,temp);",x,y,temp);return;return;

}}public static void Main()public static void Main(){{

Console.WriteLine("the max of 6 and 8 is: {0}Console.WriteLine("the max of 6 and 8 is: {0}。。 ",",max(6,8));max(6,8));

WriteMin(6,8);WriteMin(6,8);}}

}}}}

Page 41: 第三章  C# 面向对象初级编程

33、按、按 Ctrl + F5Ctrl + F5编译并运行该程序,效果如图编译并运行该程序,效果如图 3-23-2所示。图 所示。图 3- 2 3- 2 程序运行结果程序运行结果

图 3-2 程序运行结果

Page 42: 第三章  C# 面向对象初级编程

如果在如果在 returnreturn 后不跟随任何值,方法返回值是后不跟随任何值,方法返回值是 voivoidd型的。型的。

类的成员方法的重载也是类似的。类中两个以上的类的成员方法的重载也是类似的。类中两个以上的方法方法 ((包括隐藏的继承而来的方法包括隐藏的继承而来的方法 )),取的名字相同,,取的名字相同,只要使用的参数类型或者参数个数不同,编译器便知只要使用的参数类型或者参数个数不同,编译器便知道在何种情况下应该调用哪个方法,这就叫做方法的道在何种情况下应该调用哪个方法,这就叫做方法的重载。重载。

方法重载得格式就是在一个类中两次或多次定义同方法重载得格式就是在一个类中两次或多次定义同名的方法,这些同名的方法也包括从基类中继承而来名的方法,这些同名的方法也包括从基类中继承而来的方法。这些方法的名称相同,但每个方法的参数类的方法。这些方法的名称相同,但每个方法的参数类型或个数不同,这样便于系统进行区分。型或个数不同,这样便于系统进行区分。

Page 43: 第三章  C# 面向对象初级编程

其实,我们非常熟悉的其实,我们非常熟悉的 ConsoleConsole 类之所以能够实现对字符串进行格类之所以能够实现对字符串进行格式化的功能,就是因为已定义了多个重载的成员方法:式化的功能,就是因为已定义了多个重载的成员方法:

public static void WriteLinepublic static void WriteLine ()() ;;public static void WriteLine(int);public static void WriteLine(int);public static void WriteLine(float);public static void WriteLine(float);public static void WriteLine(long);public static void WriteLine(long);public static void WriteLine(uint);public static void WriteLine(uint);public static void WriteLine(char);public static void WriteLine(char);public static void WriteLine(bool);public static void WriteLine(bool);public static void WriteLine(double);public static void WriteLine(double);public static void WriteLine(char[]);public static void WriteLine(char[]);public static void WriteLine(string);public static void WriteLine(string);public static void WriteLine(Object);public static void WriteLine(Object);public static void WriteLine(ulong);public static void WriteLine(ulong);public static void WriteLine(string, Object[]);public static void WriteLine(string, Object[]);public static void WriteLine(string, Object);public static void WriteLine(string, Object);public static void WriteLine(char[], int, int);public static void WriteLine(char[], int, int);public static void WriteLine(string, Object, Object);public static void WriteLine(string, Object, Object);public static void WriteLine(string, Object, Object, Object);public static void WriteLine(string, Object, Object, Object);

Page 44: 第三章  C# 面向对象初级编程

案例案例:学生类中包含有学生姓名、性别、年龄、体重等信息。:学生类中包含有学生姓名、性别、年龄、体重等信息。我们比较学生之间的年龄和体重。我们比较学生之间的年龄和体重。目标目标:说明重载的使用基本方法:说明重载的使用基本方法

步骤步骤::11 、启动、启动 VS.NETVS.NET,新建一个控制台应用程序,名称填写为“,新建一个控制台应用程序,名称填写为“ StudentCStudentC

ompareTest”ompareTest” ,位置设置为“,位置设置为“ c:\CSharpSamples\chp3”c:\CSharpSamples\chp3” 。。22 、在代码设计窗口中编辑、在代码设计窗口中编辑 Class1.csClass1.cs 。在其中的代码如下:。在其中的代码如下: using System;using System; namespace StudentCompareTestnamespace StudentCompareTest { class Student//{ class Student//定义学生类定义学生类

{{public string s_name;public string s_name;public int s_age;public int s_age;public float s_weight;public float s_weight;public Student(string n,int a,float w)public Student(string n,int a,float w){{

Page 45: 第三章  C# 面向对象初级编程

s_name=n;s_name=n;s_age=a;s_age=a;s_weight=w;s_weight=w;

}}

public int max_age(int x, int y)public int max_age(int x, int y){{

if(x>y) return x;if(x>y) return x;else return y;else return y;

}}public float max_weight(float x, float y) public float max_weight(float x, float y) {{

if(x>y) return x;if(x>y) return x;else return y;else return y;

}}}}

Page 46: 第三章  C# 面向对象初级编程

class Testclass Test{ public static void Main(){ public static void Main()

{ Student s1=new Student("Mike",21,70);{ Student s1=new Student("Mike",21,70);Student s2=new Student("John",21,70);Student s2=new Student("John",21,70);if(s1.max_age(s1.s_age,s2.s_age)==s1.s_age)if(s1.max_age(s1.s_age,s2.s_age)==s1.s_age)

Console.WriteLine("{0}'s age is bigger than {1}'s", Console.WriteLine("{0}'s age is bigger than {1}'s", s1.s_name,s2.s_name);s1.s_name,s2.s_name);

elseelseConsole.WriteLine("{0}'s age is smaller than{1}'s", Console.WriteLine("{0}'s age is smaller than{1}'s",

s1.s_name,s2.s_name);s1.s_name,s2.s_name);if(s1.max_weight(s1.s_weight,s2.s_weight)==s1.s_weight)if(s1.max_weight(s1.s_weight,s2.s_weight)==s1.s_weight)

Console.WriteLine("{0}'s weight is bigger Console.WriteLine("{0}'s weight is bigger than{1}'s", s1.s_name,s2.s_name);than{1}'s", s1.s_name,s2.s_name);

elseelseConsole.WriteLine("{0}'s weight is smaller Console.WriteLine("{0}'s weight is smaller

than{1}'s",s1.s_name,s2.s_name);than{1}'s",s1.s_name,s2.s_name);}}

}}}}

Page 47: 第三章  C# 面向对象初级编程

55、按、按 Ctrl + F5Ctrl + F5编译并运行该程序,效果如图编译并运行该程序,效果如图 3-43-4所示。所示。

图 3-4 程序运行结果

Page 48: 第三章  C# 面向对象初级编程

3.2.5 3.2.5 销毁对象与析构函数销毁对象与析构函数在类的实例超出范围时,我们希望确保它所占的存储能被收回。在类的实例超出范围时,我们希望确保它所占的存储能被收回。 CC

## 中提供了析构函数,用于专门释放被占用的系统资源。中提供了析构函数,用于专门释放被占用的系统资源。析构函数的名字与类名相同,只是在前面加了一个符号“~”。析构函数的名字与类名相同,只是在前面加了一个符号“~”。

析构函数不接受任何参数,也不返回任何值。如果你试图声明其已任析构函数不接受任何参数,也不返回任何值。如果你试图声明其已任何一个以符号“~”开头而不与类名相同的方法,和试图让析构函数何一个以符号“~”开头而不与类名相同的方法,和试图让析构函数返回一个值一样,编译器都会产生一个错误。返回一个值一样,编译器都会产生一个错误。

析构函数不能是继承而来的,也不能显式地调用。当某个类的实析构函数不能是继承而来的,也不能显式地调用。当某个类的实例被认为不再有效,符合析构的条件,析构函数就可能在某个时刻被例被认为不再有效,符合析构的条件,析构函数就可能在某个时刻被执行。执行。 C++C++的程序员常常需要在析构函数中写上一系列的程序员常常需要在析构函数中写上一系列 deletedelete 语句语句来释放存储,而在来释放存储,而在 C#C# 中,我们不必再为此担心了。垃圾收集器会帮中,我们不必再为此担心了。垃圾收集器会帮助我们完成这些易被遗忘的工作。助我们完成这些易被遗忘的工作。

虽然虽然 C#C# (更确切的说是(更确切的说是 CLRCLR )提供了一种新的内存管理机制)提供了一种新的内存管理机制 ------自动内存管理机制(自动内存管理机制( Automatic memory managementAutomatic memory management ),资源的),资源的释放是可以通过“垃圾回收器” 自动完成的,一般不需要用户干预,释放是可以通过“垃圾回收器” 自动完成的,一般不需要用户干预,但在有些特殊情况下还是需要用到析构函数的,如在但在有些特殊情况下还是需要用到析构函数的,如在 C#C# 中非托管资中非托管资源的释放。源的释放。

Page 49: 第三章  C# 面向对象初级编程

资源的释放一般是通过“垃圾回收器”自动完成的资源的释放一般是通过“垃圾回收器”自动完成的 ,,但具体来说,仍有些需要注意的地方:但具体来说,仍有些需要注意的地方:11、值类型和引用类型的引用其实是不需要什么“垃、值类型和引用类型的引用其实是不需要什么“垃

圾回收器”来释放内存的圾回收器”来释放内存的 ,,因为当它们出了作用域后会自因为当它们出了作用域后会自动释放所占内存,因为它们都保存在栈(动释放所占内存,因为它们都保存在栈( StackStack )中;)中;22、只有引用类型的引用所指向的对象实例才保存在、只有引用类型的引用所指向的对象实例才保存在

堆(堆( HeapHeap )中,而堆因为是一个自由存储空间,所以它)中,而堆因为是一个自由存储空间,所以它并没有像“栈”那样有生存期(“栈”的元素弹出后就代并没有像“栈”那样有生存期(“栈”的元素弹出后就代表生存期结束表生存期结束 ,, 也就代表释放了内存),并且要注意的是,也就代表释放了内存),并且要注意的是,“垃圾回收器”只对这块区域起作用。“垃圾回收器”只对这块区域起作用。

Page 50: 第三章  C# 面向对象初级编程

然而,有些情况下,当需要释放非托管资源时,就必须通过写代码的然而,有些情况下,当需要释放非托管资源时,就必须通过写代码的方式来解决。通常是使用析构函数释放非托管资源,将用户自己编写的方式来解决。通常是使用析构函数释放非托管资源,将用户自己编写的释放非托管资源的代码段放在析构函数中即可。需要注意的是,如果一释放非托管资源的代码段放在析构函数中即可。需要注意的是,如果一个类中没有使用到非托管资源,那么一定不要定义析构函数,这是因为个类中没有使用到非托管资源,那么一定不要定义析构函数,这是因为对象执行了析构函数,那么“垃圾回收器”在释放托管资源之前要先调对象执行了析构函数,那么“垃圾回收器”在释放托管资源之前要先调用析构函数,然后第二次才真正释放托管资源,这样一来,两次删除动用析构函数,然后第二次才真正释放托管资源,这样一来,两次删除动作的花销比一次大多的。下面使用一段代码来示析构函数是如何使用的:作的花销比一次大多的。下面使用一段代码来示析构函数是如何使用的:

public class ResourceHolder public class ResourceHolder {{ …… …… ~ResourceHolder()~ResourceHolder() {{ // // 这里是清理非托管资源的用户代码段这里是清理非托管资源的用户代码段 }}}}

Page 51: 第三章  C# 面向对象初级编程

3.2.63.2.6 在方法调用中传递参数在方法调用中传递参数

C#C# 中方法的参数有四种类型:中方法的参数有四种类型:◆◆值参数,不含任何修饰符。值参数,不含任何修饰符。◆◆引用型参数,以引用型参数,以 refref修饰符声明。修饰符声明。◆◆输出参数,以输出参数,以 outout 修饰符声明。修饰符声明。◆◆数组型参数,以数组型参数,以 paramsparams 修饰符声明。修饰符声明。

Page 52: 第三章  C# 面向对象初级编程

3.2.6.1 3.2.6.1 值参数值参数

当利用值向方法传递参数时,编译程序给实参当利用值向方法传递参数时,编译程序给实参的值做一份拷贝,并且将此拷贝传递给该方法。的值做一份拷贝,并且将此拷贝传递给该方法。被调用的方法不会修改内存中实参的值,所以使被调用的方法不会修改内存中实参的值,所以使用值参数时,可以保证实际值是安全的。在调用用值参数时,可以保证实际值是安全的。在调用方法时,如果形式化参数的类型是值参数的话,方法时,如果形式化参数的类型是值参数的话,调用的实参的表达式必须保证是正确的值表达式。调用的实参的表达式必须保证是正确的值表达式。

Page 53: 第三章  C# 面向对象初级编程

案例案例:两个数交换:两个数交换 11目标目标:掌握值参数进行交换:掌握值参数进行交换步骤步骤::11、启动、启动 VS.NETVS.NET,新建一个控制台应用程序,名称填写为,新建一个控制台应用程序,名称填写为

““ SwapTest1”SwapTest1” ,位置设置为“,位置设置为“ c:\CSharpSamples\chc:\CSharpSamples\chp3”p3” 。。

22、在代码设计窗口中编辑、在代码设计窗口中编辑 Class1.csClass1.cs 。在其中的代码如下:。在其中的代码如下: using System;using System; namespace SwapTest1namespace SwapTest1 { class Test{ class Test

{ static void Swap(int x, int y){ static void Swap(int x, int y) {{

int temp=x;int temp=x;x=y;x=y;y=temp;y=temp;

}}

Page 54: 第三章  C# 面向对象初级编程

static void Main()static void Main(){{

int i=1,j=2;int i=1,j=2;Swap(i, j);Swap(i, j);

Console.WriteLine("i={0}, j={1}",i,j);Console.WriteLine("i={0}, j={1}",i,j);}}

}}}}

Page 55: 第三章  C# 面向对象初级编程

33、按、按 Ctrl + F5Ctrl + F5编译并运行该程序,效果如图编译并运行该程序,效果如图 3-53-5所示。所示。

图 3-5 程序运行结果

Page 56: 第三章  C# 面向对象初级编程

3.2.6.2 3.2.6.2 引用型参数引用型参数

和值参不同的是,引用型参数并不开辟新的内和值参不同的是,引用型参数并不开辟新的内存区域。当利用引用型参数向方法传递形参时,存区域。当利用引用型参数向方法传递形参时,编译程序将把实际值在内存中的地址传递给方法。编译程序将把实际值在内存中的地址传递给方法。

Page 57: 第三章  C# 面向对象初级编程

案例案例:两个数交换:两个数交换 22目标目标:掌握引用型参数进行交换:掌握引用型参数进行交换

步骤步骤::11、启动、启动 VS.NETVS.NET,新建一个控制台应用程序,名称填写为,新建一个控制台应用程序,名称填写为

““ SwapTest2”SwapTest2” ,位置设置为“,位置设置为“ c:\CSharpSamples\chc:\CSharpSamples\chp3”p3” 。。

22、在代码设计窗口中编辑、在代码设计窗口中编辑 Class1.csClass1.cs 。在其中的代码如下:。在其中的代码如下: using System;using System; namespace SwapTest2namespace SwapTest2 {{

class Testclass Test {{

static void Swap(ref int x, ref int y)static void Swap(ref int x, ref int y) {{

Page 58: 第三章  C# 面向对象初级编程

int temp=x;int temp=x;x=y;x=y;y=temp;y=temp;

}}static void Main()static void Main(){{

int i=1,j=2;int i=1,j=2;Swap(ref i, ref j);Swap(ref i, ref j);Console.WriteLine("i={0}, j={1}",i,j);Console.WriteLine("i={0}, j={1}",i,j);

}}}}

}}

Page 59: 第三章  C# 面向对象初级编程

33、按、按 Ctrl + F5Ctrl + F5编译并运行该程序,效果如编译并运行该程序,效果如图图 3-63-6 所示。所示。

图 3-6 程序运行结果

Page 60: 第三章  C# 面向对象初级编程

MainMain 函数中调用了函数中调用了 SwapSwap 函数,函数, xx代表代表 ii ,, yy代表代表 jj 。这样,调用成功地实现了。这样,调用成功地实现了 ii和和 jj 的值交换。的值交换。

在方法中使用引用型参数,会经常可能导致多个变量名指向同一处内存地址。见在方法中使用引用型参数,会经常可能导致多个变量名指向同一处内存地址。见下例:下例:

class Aclass A{ string s;{ string s; void F(ref string a, ref string b)void F(ref string a, ref string b) { s="One"{ s="One" a="Two"a="Two" b="Three"b="Three" }} void G()void G() { F(ref s, ref s);{ F(ref s, ref s); }}}}在方法在方法 GG对对 FF的调用过程中,的调用过程中, ss 的引用被同时传递给了的引用被同时传递给了 aa 和和 bb 。此时。此时 ss ,, aa ,, bb同同

时指向了同一块内存区域。时指向了同一块内存区域。

Page 61: 第三章  C# 面向对象初级编程

3.2.6.3 3.2.6.3 输出参数输出参数

与引用型参数类似,输出型参数也不开辟新与引用型参数类似,输出型参数也不开辟新的内存区域。与引用型参数的差别在于,调用方的内存区域。与引用型参数的差别在于,调用方法前无需对变量进行初始化。输出型参数用于传法前无需对变量进行初始化。输出型参数用于传递方法返回的数据。递方法返回的数据。

outout 修饰符后应跟随与形参的类型相同的类修饰符后应跟随与形参的类型相同的类型声明。在方法返回后,传递的变量被认为经过型声明。在方法返回后,传递的变量被认为经过了初始化。了初始化。

Page 62: 第三章  C# 面向对象初级编程

案例案例:输出参数:输出参数目标目标:掌握输出参数进行传送:掌握输出参数进行传送11、启动、启动 VS.NETVS.NET,新建一个控制台应用程序,名称填写为,新建一个控制台应用程序,名称填写为

““ OutTest”OutTest” ,位置设置为“,位置设置为“ c:\CSharpSamples\chpc:\CSharpSamples\chp3”3” 。。

22、在代码设计窗口中编辑、在代码设计窗口中编辑 Class1.csClass1.cs 。在其中的代码如下:。在其中的代码如下:

using System;using System; namespace OutTestnamespace OutTest { class Test{ class Test

{static void SplitPath(string path, out string dir, o{static void SplitPath(string path, out string dir, out string name)ut string name)

{{int i=path.Length;int i=path.Length;while (i>0)while (i>0){{

Page 63: 第三章  C# 面向对象初级编程

char ch=path[i-1];char ch=path[i-1];if (ch=='\\'||ch=='/'||ch==':')break;if (ch=='\\'||ch=='/'||ch==':')break;i--;i--;}}

dir=path.Substring(0, i);dir=path.Substring(0, i);name=path. Substring(i);name=path. Substring(i);

}}static void Main()static void Main(){ string dir, name;{ string dir, name;SplitPath("c:\\Windows\\Systam\\hello.txt", out dir, out namSplitPath("c:\\Windows\\Systam\\hello.txt", out dir, out nam

e);e);Console.WriteLine(dir);Console.WriteLine(dir);Console.WriteLine(name);Console.WriteLine(name);

}}}}

}}

Page 64: 第三章  C# 面向对象初级编程

33、按、按 Ctrl + F5Ctrl + F5编译并运行该程序,效果如图编译并运行该程序,效果如图 3-73-7所示。所示。

我们注意到,变量我们注意到,变量 dirdir 和和 namename 在传递给在传递给 SplitPathSplitPath之之前并未初始化,在调用之后它们则有了明确的值。前并未初始化,在调用之后它们则有了明确的值。

图 3-7 程序运行结果

Page 65: 第三章  C# 面向对象初级编程

3.2.6.4 3.2.6.4 数组型参数数组型参数

如果形参表中包含了数组型参数,那么它必须如果形参表中包含了数组型参数,那么它必须在参数表中位于最后。另外,参数只允许是一维在参数表中位于最后。另外,参数只允许是一维数组。比如,数组。比如, string[]string[]和和 string[][]string[][]类型都可以作类型都可以作为数组型参数,而为数组型参数,而 string[,]string[,]则不能。最后,数组则不能。最后,数组型参数不能再有型参数不能再有 refref和和 outout 修饰符。修饰符。

Page 66: 第三章  C# 面向对象初级编程

案例案例:数组型参数:数组型参数目标目标:掌握数组型参数进行传送:掌握数组型参数进行传送

步骤步骤::11、启动、启动 VS.NETVS.NET,新建一个控制台应用程序,名称填写为“,新建一个控制台应用程序,名称填写为“ ArrArr

ayTest”ayTest” ,位置设置为“,位置设置为“ c:\CSharpSamples\chp3”c:\CSharpSamples\chp3” 。。22、在代码设计窗口中编辑、在代码设计窗口中编辑 Class1.csClass1.cs 。在其中的代码如下:。在其中的代码如下: using System;using System; namespace ArrayTestnamespace ArrayTest {{

class Testclass Test {{

static void F(params int[] args)static void F(params int[] args) {{

Page 67: 第三章  C# 面向对象初级编程

Console.WriteLine("Array contains {0} elementsConsole.WriteLine("Array contains {0} elements:: ",args.Length);",args.Length);foreach(int i in args)foreach(int i in args)Console.Write(" {0}", i);Console.Write(" {0}", i);Console.WriteLine();Console.WriteLine();}}public static void Main()public static void Main(){{int[] a={1, 2, 3};int[] a={1, 2, 3};F(a);F(a);F(10, 20, 30, 40);F(10, 20, 30, 40);F();F();}}

}}}}

Page 68: 第三章  C# 面向对象初级编程

33、按、按 Ctrl + F5Ctrl + F5编译并运行该程序,效果如图编译并运行该程序,效果如图 3-83-8所示。所示。

图 3-8 程序运行结果

Page 69: 第三章  C# 面向对象初级编程

在上例中,第一次调用在上例中,第一次调用 FF是简单地把数组是简单地把数组 aa作为值参数传递;第二次调用把己给出数值的数作为值参数传递;第二次调用把己给出数值的数组传递给了组传递给了 FF ;而在第一次调用中,;而在第一次调用中, FF 创建了含创建了含有有 00个元素的整型数组作为参数传递。后两次调个元素的整型数组作为参数传递。后两次调用完整的写法应该是:用完整的写法应该是:F(new int[]{10, 20, 30, 40});F(new int[]{10, 20, 30, 40});F(new int[]{});F(new int[]{});

Page 70: 第三章  C# 面向对象初级编程

3.2.73.2.7 静态对象成员静态对象成员

C#C# 的类定义中可以包含两种方法的类定义中可以包含两种方法 :: 静态的和非静态的和非静态的。使用了静态的。使用了 staticstatic 修饰符的方法为静态方法,修饰符的方法为静态方法,反之则是非静态的。反之则是非静态的。

静态方法是一种特殊的成员方法,它不属于类静态方法是一种特殊的成员方法,它不属于类的某一个具体的实例。非静态方法可以访问类中的某一个具体的实例。非静态方法可以访问类中的任何成员,而静态方法只能访问类中的静态成的任何成员,而静态方法只能访问类中的静态成员。看这个例子:员。看这个例子:

Page 71: 第三章  C# 面向对象初级编程

class Aclass A{{ int;int; static int y;static int y; static int F()static int F() {{ x=1;// x=1;// 错误,不允许访问错误,不允许访问 y=2;//y=2;// 正确,允许访问正确,允许访问 }}}}

Page 72: 第三章  C# 面向对象初级编程

在这个类定义中,静态方法在这个类定义中,静态方法 F()F()可以访问类中可以访问类中静态成员静态成员 ss ,但不能访问非静态的成员,但不能访问非静态的成员 xx。这是。这是因为,因为, xx作为非静态成员,在类的每个实例中都作为非静态成员,在类的每个实例中都占有一个存储占有一个存储 (( 或者说具有一个副本或者说具有一个副本 )),而静态方,而静态方法是类所共享的,它无法判断出当前的法是类所共享的,它无法判断出当前的 xx 属十哪属十哪个类的实例,所以不知道应该到内存的哪个地址个类的实例,所以不知道应该到内存的哪个地址去读取当前去读取当前 xx的值。而的值。而 yy是非静态成员,所有类是非静态成员,所有类的实例都公用一个副本,静态方法的实例都公用一个副本,静态方法 FF使用已就不使用已就不存在什么问题。存在什么问题。

Page 73: 第三章  C# 面向对象初级编程

那么,是不是静态方法就无法识别类的实例了呢那么,是不是静态方法就无法识别类的实例了呢 ?? 在在 C#C# 中,我们可以灵活地中,我们可以灵活地采用传递参数的办法。例如:采用传递参数的办法。例如:

程序清单:程序清单:using Systemusing Systemclass Windowclass Window{ public string m_caption{ public string m_caption;; //// 窗口的标题窗口的标题 public bool IsActive;//public bool IsActive;// 窗口是台被激活窗口是台被激活 public handle m_handle;//public handle m_handle;// 窗口的句柄窗口的句柄 public static int m_total; //public static int m_total; //当前打开的窗口数目当前打开的窗口数目 public handle Windowpublic handle Window()() { m_total++;//{ m_total++;// 窗口总数加窗口总数加11 //……//……创建窗口的一些执行代码创建窗口的一些执行代码 return m_handlereturn m_handle;; //// 窗口的返回值们为句柄窗口的返回值们为句柄 }}~Window()~Window() {{

Page 74: 第三章  C# 面向对象初级编程

m_total--//m_total--// 窗口总数减窗口总数减 11 //……//……撤消窗口的一些执行代码撤消窗口的一些执行代码 }} public static string GetWindowCaption(Window w)public static string GetWindowCaption(Window w) { return w.m_caption;{ return w.m_caption; }} //……//……窗口的其它成员窗口的其它成员}}分析一下上面例子中的代码。每个窗口都有窗口标题分析一下上面例子中的代码。每个窗口都有窗口标题 m_captionm_caption 、窗口句、窗口句

柄柄 m_handlem_handle 、窗口是否激活、窗口是否激活 IsActiveIsActive 三个非静态的数据成员三个非静态的数据成员 (( 窗口句柄是窗口句柄是WiWindowsndows 操作系统中保存窗口相关信急的一种数据结构,我们在这个例子中简化操作系统中保存窗口相关信急的一种数据结构,我们在这个例子中简化了对句柄的使用了对句柄的使用 ))。系统中总共打开的窗口数目。系统中总共打开的窗口数目 m_totalm_total 作为一个静态成员。作为一个静态成员。每个窗口调用构造函数创建,这时每个窗口调用构造函数创建,这时 m_totalm_total 的值加的值加11。窗口关闭或因为其它行。窗口关闭或因为其它行为撤消时,通过析构函数为撤消时,通过析构函数 m_totalm_total 的值减的值减 11。。

我们要注意窗口类的静态方法我们要注意窗口类的静态方法 GetWindowCaption(Window w)GetWindowCaption(Window w)。这里已。这里已通过参数通过参数ww将对象传递给方法执行,这样已就可以通过具体的类的实例指明调将对象传递给方法执行,这样已就可以通过具体的类的实例指明调用的对象,这时它可以访问具体实例中的成员,无论是静态成员还是非静态成用的对象,这时它可以访问具体实例中的成员,无论是静态成员还是非静态成员。员。

Page 75: 第三章  C# 面向对象初级编程

3.2.83.2.8 用属性封装数据用属性封装数据

属性是对现实世界中实体特征的抽象,它提供了对类属性是对现实世界中实体特征的抽象,它提供了对类或对象性质的访问。比如,一个用户的姓名、一个文件的或对象性质的访问。比如,一个用户的姓名、一个文件的大小、一个窗口的标题,都可以作为属性。类的属性所描大小、一个窗口的标题,都可以作为属性。类的属性所描述的是状态信息,在类的某个实例中属性的值表不该对象述的是状态信息,在类的某个实例中属性的值表不该对象的状态值。的状态值。

C#C# 中的属性更充分地体现了对象的封装性:不直接操中的属性更充分地体现了对象的封装性:不直接操作类的数据内容,而是通过访问器进行访问。它借助于作类的数据内容,而是通过访问器进行访问。它借助于 ggetet 和和 setset 对属性的值进行读写,这在对属性的值进行读写,这在 C++C++中是需要程序员中是需要程序员手工完成的一项工作。手工完成的一项工作。

Page 76: 第三章  C# 面向对象初级编程

3.2.8.1 3.2.8.1 声明声明属性采用如下方式进行声明:属性采用如下方式进行声明: [[ 属性修饰符属性修饰符 ] ] 属性的类型 属性名称 属性的类型 属性名称 {{ 访问声明访问声明 }}属性修饰符有属性修饰符有 newnew、、 publicpublic 、、 protectedprotected 、、 internalinternal 、、

privateprivate 、、 staticstatic 、、 virtualvirtual 、、 overrideoverride 和和 abstractabstract 。。若属性声明中含有若属性声明中含有 staticstatic 修饰符,这个属性就被称作静修饰符,这个属性就被称作静

态属性。当没有态属性。当没有 staticstatic 修饰符时,这个属性被称为实例属性修饰符时,这个属性被称为实例属性(非静态属性)。一个静态属性与指定的实例无关,并且在(非静态属性)。一个静态属性与指定的实例无关,并且在静态属性的访问器中使用静态属性的访问器中使用 thisthis 是错误的。在一个静态属性中是错误的。在一个静态属性中包括包括 virtualvirtual 、、 abstractabstract或或 overrideoverride 修饰符也是错误的。一修饰符也是错误的。一个实例属性与一个类中给定的实例相关,并且这个实例可以个实例属性与一个类中给定的实例相关,并且这个实例可以被属性访问器中的被属性访问器中的 thisthis访问。当属性在形式为访问。当属性在形式为 E.ME.M的成员的成员访问中被引用时,如果访问中被引用时,如果 MM是一个静态属性,是一个静态属性, EE 必须表示一必须表示一个类,而如果个类,而如果 MM是一个实例属性,是一个实例属性, EE 必须表示一个实例。必须表示一个实例。

Page 77: 第三章  C# 面向对象初级编程

若实例属性声明中包括若实例属性声明中包括 virtualvirtual 修饰符,这个属性就被称为虚修饰符,这个属性就被称为虚属性。当没有属性。当没有 virtualvirtual 修饰符时,这个属性就被称为非虚属性。非修饰符时,这个属性就被称为非虚属性。非虚属性的执行是不变的,不管属性是否在所声明的类的实例或派虚属性的执行是不变的,不管属性是否在所声明的类的实例或派生类的实例中被访问,执行都是相同的。相反,虚属性的执行可生类的实例中被访问,执行都是相同的。相反,虚属性的执行可以被派生类改变。改变继承的虚方法的执行过程称为属性重载。以被派生类改变。改变继承的虚方法的执行过程称为属性重载。

若实例属性声明中包括若实例属性声明中包括 overrideoverride 修饰符,这个属性就被称为修饰符,这个属性就被称为覆盖属性。覆盖属性用相同的声明覆盖一个继承虚属性。然而虚覆盖属性。覆盖属性用相同的声明覆盖一个继承虚属性。然而虚属性声明引入新属性,覆盖属性通过提供这个属性访问器或访问属性声明引入新属性,覆盖属性通过提供这个属性访问器或访问器的新执行来对存在的继承的虚属性进行特殊化。器的新执行来对存在的继承的虚属性进行特殊化。

若实例属性声明中包括若实例属性声明中包括 abstractabstract 修饰符,这个属性就被称为修饰符,这个属性就被称为抽象属性。抽象属性隐含地也是一个虚属性。抽象属性声明引入抽象属性。抽象属性隐含地也是一个虚属性。抽象属性声明引入新虚属性,但是没有提供属性访问器或访问器的执行。作为替代,新虚属性,但是没有提供属性访问器或访问器的执行。作为替代,非抽象派生类需要为访问器或覆盖的属性的访问器提供它们自己非抽象派生类需要为访问器或覆盖的属性的访问器提供它们自己的执行。因为一个抽象属性的访问器不提供实际执行,它的访问的执行。因为一个抽象属性的访问器不提供实际执行,它的访问器主体就完全由分号组成。器主体就完全由分号组成。

Page 78: 第三章  C# 面向对象初级编程

例如:例如:class Aclass A{{ public abstrat int P {get;set;}public abstrat int P {get;set;}}}

Page 79: 第三章  C# 面向对象初级编程

3.2.8.2 3.2.8.2 属性的访问属性的访问在属性的访问声明中,对属性的值的读操作用在属性的访问声明中,对属性的值的读操作用 getget 关键字标出,关键字标出,

对属性的值的写操作用对属性的值的写操作用 setset 关键字标出。关键字标出。案例案例:对属性的操作:对属性的操作目标目标:掌握属性访问的基本方法:掌握属性访问的基本方法步骤步骤::11、启动、启动 VS.NETVS.NET,新建一个控制台应用程序,名称填写为“,新建一个控制台应用程序,名称填写为“ FilFil

eTest”eTest” ,位置设置为“,位置设置为“ c:\CSharpSamples\chp3”c:\CSharpSamples\chp3” 。。22、在代码设计窗口中编辑、在代码设计窗口中编辑 Class1.csClass1.cs 。在其中的代码如下:。在其中的代码如下:using System;using System;namespace FileTestnamespace FileTest{ public class File{ public class File{ private string s_filename;{ private string s_filename;

public string Filenamepublic string Filename{{

getget{ return s_filename;{ return s_filename;

Page 80: 第三章  C# 面向对象初级编程

}}

setset{ if(s_filename!=value){ if(s_filename!=value){{s_filename=value;s_filename=value;}}}}}}}}public class Testpublic class Test{ public static void Main(){ public static void Main(){{File f=new File();File f=new File();f.Filename="myfile.txt";f.Filename="myfile.txt";string s=f.Filename;string s=f.Filename;}}}}

}}

Page 81: 第三章  C# 面向对象初级编程

33、完成了对属性的操作。、完成了对属性的操作。在属性的访问声明中:在属性的访问声明中:◆◆只有只有 setset访问器,表明属性的值只能进行设置而不能读出。访问器,表明属性的值只能进行设置而不能读出。◆◆只有只有 getget访问器,表明属性的值是只读的,不能改写。访问器,表明属性的值是只读的,不能改写。◆◆同时具有同时具有 setset访问器和访问器和 getget访问器,表明属性的值的读写都是访问器,表明属性的值的读写都是

允许的。允许的。除了使用了除了使用了 abstractabstract 修饰符的抽象属性,每个访问器的执行体中修饰符的抽象属性,每个访问器的执行体中

只有分号“只有分号“ ;”;” ,其它所有属性的,其它所有属性的 getget访问器都通过访问器都通过 returnreturn 来读取属来读取属性的值,性的值, setset访问器都通过访问器都通过 valuevalue 来设置属性的值。来设置属性的值。

举个例子,旅馆对住宿人员进行登记,要记录的信息有举个例子,旅馆对住宿人员进行登记,要记录的信息有 ::客人姓名、客人姓名、性别、所住的房间号、己住宿的人数。这里,客人的姓名和性别一经性别、所住的房间号、己住宿的人数。这里,客人的姓名和性别一经确定就不能再更改了,用户可以要求改变房间,住宿的人数当然也是确定就不能再更改了,用户可以要求改变房间,住宿的人数当然也是不断变化的。我们在类的构造函数中对客人的姓名和性别进行初始化,不断变化的。我们在类的构造函数中对客人的姓名和性别进行初始化,在四个属性中,客人的姓名和性别是只读的,故只具有在四个属性中,客人的姓名和性别是只读的,故只具有 getget访问器访问器 ;;房房间号和住宿人数允许改变,同时具有间号和住宿人数允许改变,同时具有 setset访问器和访问器和 getget访问器。访问器。

Page 82: 第三章  C# 面向对象初级编程

程序清单:程序清单:using System;using System;namespace CustomerTestnamespace CustomerTest{ public class Customer1{ public class Customer1

{ public enum sex{ public enum sex{ man,{ man,woman,woman,};};private string s_name;private string s_name;public string Namepublic string Name{ get{ get{{return s_name;return s_name;}}}}

private sex m_sex;private sex m_sex;public sex Sexpublic sex Sex

{{

Page 83: 第三章  C# 面向对象初级编程

getget{{

return m_sex;return m_sex;}}

}}private string s_no;private string s_no;public string Nopublic string No{ get{ get

{{return s_no;return s_no;

}}setset{ if (s_no!=value){ if (s_no!=value)

{ s_no=value;{ s_no=value;}}

}}}}

private int i_day;private int i_day;

Page 84: 第三章  C# 面向对象初级编程

public int Day public int Day { get{ get

{ return i_day;{ return i_day;}}setset{ if (i_day!=value){ if (i_day!=value){{i_day=value;i_day=value;}}}}

}}public void Customer(string name,sex sex,string no,int day)public void Customer(string name,sex sex,string no,int day){ s_name=name;{ s_name=name;

m_sex=sex;m_sex=sex;s_no=no;s_no=no;i_day=day;i_day=day;

}}}}

}}

Page 85: 第三章  C# 面向对象初级编程

实训三实训三

11、定义一个类,并完成对该类构造函数和、定义一个类,并完成对该类构造函数和析构函数的创建,体会这两个函数对类的影响。析构函数的创建,体会这两个函数对类的影响。

22、在一个类中对方法进行重载,体会重载、在一个类中对方法进行重载,体会重载函数之间参数不同的意义。函数之间参数不同的意义。

33、在方法调用中传递参数的方法有哪些。、在方法调用中传递参数的方法有哪些。

Page 86: 第三章  C# 面向对象初级编程

本章小结本章小结 这一章我们介绍了这一章我们介绍了 C#C# 中的类的基本概念,展示了类的各种组中的类的基本概念,展示了类的各种组

成。成。 C#C# 中的类是对数据结构的封装与抽象,是中的类是对数据结构的封装与抽象,是 C#C#最重要的组成部最重要的组成部

分。我们利用类定义各种新的数据类型,其中即包含了数据的内分。我们利用类定义各种新的数据类型,其中即包含了数据的内容,又包含对数据内容的操作。封装之后,类可以控制外界对已容,又包含对数据内容的操作。封装之后,类可以控制外界对已的成员的访问。的成员的访问。

类的静态成员属于该类,非静态成员则属于这个类的某个实例。类的静态成员属于该类,非静态成员则属于这个类的某个实例。 在一个类的实例—对象的生命周期中,最先执行的代码就是类在一个类的实例—对象的生命周期中,最先执行的代码就是类

的构造函数。构造函数是用来初始化对象的特殊类型的函数。的构造函数。构造函数是用来初始化对象的特殊类型的函数。 不带参数的构造函数对类的实例的初始化是固定的。我们也可不带参数的构造函数对类的实例的初始化是固定的。我们也可

以使用带参数的构造函数,通过向已传递参数来对类的不同实例以使用带参数的构造函数,通过向已传递参数来对类的不同实例进行不同的初始化。构造函数同样可以使用默认参数。进行不同的初始化。构造函数同样可以使用默认参数。

当这个类的实例超出作用域时,或者由于其它理由被破坏时,当这个类的实例超出作用域时,或者由于其它理由被破坏时,析构函数将释放分配给该实例的任何存储区。析构函数将释放分配给该实例的任何存储区。

Page 87: 第三章  C# 面向对象初级编程

练习与提高三练习与提高三

11、类和对象的关系是什么?用具体的例子、类和对象的关系是什么?用具体的例子说明二者的联系和区别?说明二者的联系和区别?

22、如何实现对类的重用?在类的设计中,、如何实现对类的重用?在类的设计中,主要的成员是那些?主要的成员是那些?

33、类的静态成员和非静态成员有哪些区别?、类的静态成员和非静态成员有哪些区别?44、构造函数和析构函数分别有什么意义?、构造函数和析构函数分别有什么意义?