《2022年第章面向对象编程技术》由会员分享,可在线阅读,更多相关《2022年第章面向对象编程技术(25页珍藏版)》请在金锄头文库上搜索。
1、1 / 25 第4章 面向对象编程技术面向对象的程序设计方法是当前程序设计的大势所趋。面向对象的程序设计方法是对结构化程序设计方法的重新认识。在程序的设计中,方法总是与特定的数据结构密切相关的,方法含有对数据结构的访问,特定的方法只适用于特定的数据结构,因此方法与数据结构在编程中应该是一个密不可分的整体,这个整体叫对象。C#通过类、对象、继承、多态等机制形成一个完善的面向对象的编程体系。4.1 类和对象类是 C#程序设计的基本单位。4.1.1 类和对象概述现实生活中的类是人们对客观对象不断认识而产生的抽象的概念,而对象则是现实生活中的一个个实体。例如,人们在现实生活中接触了大量的汽车、摩托车、
2、自行车等实体,从而产生了交通工具的概念,交通工具就是一个类,而现实生活中的具体的汽车、摩托车自行车等则是该类的对象。面向对象程序设计的类从本质上和人们现实生活中的这一认识过程是相同的。例如在编程实践中,人们经常使用按钮这一控件,每一个具体的按钮是一个按钮对象,而按钮类则是按钮对象的抽象,并且人们把这一抽象用计算机编程语言表示为数据集合与方法集合的统一体,然后再用这个类创建一个个具体的按钮对象。可以把类比作一种蓝图,而对象则是根据蓝图所创建的实例,可以把类比作生产模具,而对象则是由这种模具产生的实例(产品)。所以人们又把对象叫做类的实例。类是对事物的定义,而对象则是该事物本身。在 Visual
3、Studio.NET集成环境中的,工具箱中的一个个控件,是被图形文字化的可视的类,而把这些控件添加到窗体设计器中后,窗体设计器中的控件则是对象,即由工具箱中的类创建的对象。类是一种数据类型,在C#中,类这种数据类型可以分为两种:一种是由系统提供的预先定义的,这些类在.NET 框架类库中;一种是用户定义数据类型。在创建对象之前必须先定义该对象所属的类。然后由类声明对象。类本质上是一种数据类型,所以类的用法与基本数据类型的用法基本相同,事实上,在Visual Studio.NET中像int、 float 等基本数据类型也是特殊的类。那么,用基本数据类型可以声明变量,用类类型也可以声明变量,只不过类
4、类型的变量叫类的对象或类的实例。精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 1 页,共 25 页2 / 25 4.1.2 类定义类定义的格式与结构定义的格式相似,在类定义中需要使用关键字class,其简单的定义格式为:class 类名 类体 “类名”是一个合法的C#标识符,表示数据类型(类类型)名称,“类体”以一对大括号开始和结束,在一对大括号后面可以跟一个分号。例如:class student /类名为 student public string ID 。public string Name 。public bool Sex 。 上 例 中
5、, 定 义 了 一 个 名 为 “ student” ( 学 生 ) 的 类 , 类 体 中 包 括 学 生 的 学 号(“ ID ”)、姓名(“Name”)与性别(“Sex”)。在上例的“类体”中声明的数据都使用“public ”修饰,“public ”(公共的)表示这些数据可以直接进行访问。如果仅从“ student”类的定义形式看,其与结构类型除了关键字外几乎没有任何差别。当然这只是为了说明问题方便,这种定义方式并不符合面向对象程序设计的原则,因为类中缺少了对数据的操作。“类体”包括类中的所有数据及对数据的操作,面向对象程序设计将数据与对数据的操作作为一个整体,以类的形式进行定义,这种机
6、制叫“封装”。在“类体”中,所有的数据及对数据的操作的集合叫类成员,类成员的种类很多,本章仅介绍“字段”、“属性”、“方法”与“构造函数”。“字段”是类定义中的数据,也叫类的变量。在上例中定义的“ID ”、“ Name”与“ Sex”等均为类中的字段。“属性”用于读取和写入“字段”值,“属性”是字段的自然扩展,对用户而言,“属性”等同于“字段”本身,对程序员而言,属性是一种读写“字段”的特殊方法。“方法”实质上就是函数,用于对字段进行计算和操作,即对类中的数据进行操作,以实现特定的功能。4.1.3 声明与使用对象定义类之后,可以用定义的类声明对象,声明对象后可以访问对象成员。每一个类对象均具有
7、该类定义中的所有成员,正如每一个整型变量均可以表示同样的数值范围一样。1. 声明对象声明对象的格式与声明基本数据类型的格式相同:类名 对象名。例如:精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 2 页,共 25 页3 / 25 student S1。 /声明一个学生类对象S1 但是,对象声明后,需用“new”关键字进行初始化,这样才能为对象在内存中分配保存数据的空间。初始化的格式:对象名 =new 类名 ( )。例如:S1=new student( ) 。 /为 S1 分配内存空间可以将声明与初始化对象合二为一,例如:student S1=new
8、 student( ) 。 /声明对象并初始化2. 访问对象访问对象实质是访问对象成员,对对象变量成员的访问与结构变量相同,使用“.”运算符。例如:S1.ID=12345 。S1.Name= 张三 。S1.Sex=true。上面的代码为对象S1数据成员赋值。可以使用对象变量为另一对象变量整体赋值,例如,“student S2。S2=S1 。”或“student S2=S1。”。这时,不需要使用new关键字对“ S2”初始化。可以使用对象中的某一成员。例如:string sName=S1.Name 。 / 将对象 S1 的 Name 成员值赋给字符串变量sName 4.1.4 值类型与引用类型类
9、定义的类型与结构定义的类型虽然相似,但这两种类型却有很大的差别,这涉及到C#中变量存储方式的问题。从变量的存储方式看,C#中的变量可以分为值类型变量与引用类型变量。1. 值类型值类型变量直接包含其本身的数据,因此每个值类型变量本身就包含有赋给它的值。在C#中,内置数据类型除了字符串(string)类型与对象(object)类型外其余均为值类型。枚举类型与结构类型也是值类型。例如“int X =42。”,整型变量X 就是值类型。2. 引用类型与值类型不同,引用类型变量不包含本身的数据,只是存储对数据的引用,数据保存在内存的其他位置。例如:using System。class Class1 cla
10、ss Test public int A 。 static void Main(string args) 精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 3 页,共 25 页4 / 25 int X=42,Y=X 。Test T1=new Test()。T1.A=42 。Test T2=T1 。Y=100 。T2.A=100 。 引用类型与值类型在内存中的存储形式如图4-1 所示。int X=42 。42 T1.A=42 。42 的引用42 图4-1 值类型与引用类型的存储值类型变量可以使用变量来赋值,引用类型变量也可以使用变量来赋值。例如上例中的
11、代码:int X=42,Y=X 。Test T1=new Test() 。T1.A=42 。Test T2=T1 。 /使用对象变量赋值但是,值类型与引用类型用变量赋值的性质却不同,如图4-2 所示。int X=42 。int Y=X 。42 42 T1.A=42 。42 的引用42 Test T2=T1 。42的引用图4-2 值类型与引用类型用变量赋值从图中可以看到,用变量“X”为变量“Y”赋值,是将“X”所在内存中的值复制给了“Y”;而用变量“T1”为“ T2”赋值,则是将“T1”对数据的引用复制给了“T2 ”,即两个对象变量使用的是同一内存中的数据,也就是说,用“T1”为“ T2”赋值不
12、需要使用new 关键字对对象初始化,也就没有为对象“T2”分配存储数据的内存空间。这样,在改变变量值时,将发生本质差别。例如上例中的代码:Y=100 。 /Y 的值为 100,X 的值仍是42 T2.A=100 。 /不仅 T2 的值为 100,T1 的值也为100 4.1.5 访问控制在上面类定义的例子中,声明类的数据成员(字段)时,均使用public 进行修饰, public叫访问修饰符。声明类中的成员时,使用不同的访问修饰符,表示对类成员的访问权限不同,或者说访问修饰符确定了在什么范围可以访问类成员。精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - -
13、-第 4 页,共 25 页5 / 25 C#中最常用的访问修饰符及其意义,如表4-1所示。表 4-1 访问修饰符访问修饰符意义public (公有)访问不受限制,可以被任何其他类访问private(私有)访问只限于含该成员的类,即只有该类的其他成员能访问protected(保护)访问只限于含该成员的类、及该类的派生类在类定义中,如果声明成员没有使用任何访问修饰符,则该成员被认为是私有(private)的。如果不涉及继承,private 与 protected 没有什么区别。在类定义中,如果成员被声明为private 或 protected ,则不允许在类定义外使用点运算符访问,即在类定义外,点
14、运算符只能访问public 成员。例如在下面的类定义中:class student /类名为 student private string ID 。 /私有public string Name 。 /公有protected bool Sex 。 / 保护 student S1=new student( ) 。S1.ID=12345 。 /非法, ID 为 private S1.Name= 张三 。 / 合法, Name 为 public S1.Sex=true。 /非法, Sex 为 protected 在类定义外使用点运算符访问“ID ”与“ Sex”是非法的,而访问“Name”则是合法的,
15、因为“ ID ”与“ Sex”是 private 或 protected 的,而“ Name”则是 public 的。通常,在类定义中,数据成员(字段)被声明为private 或 protected,从而实现所谓的数据隐藏,以确保不能在类外随意访问类的数据成员;而其他种类的成员则被声明为public ,以通过这些成员实现对类的数据成员的访问。4.2 属性对私有或保护数据成员常见的访问是读取与设置修改数据值,在类定义外部,这种访问可通过属性成员实现。通过属性可以控制对数据的访问方式,甚至设置数据可接受的值域。4.2.1 声明属性在类定义中声明属性的格式为:访问修饰符类型 属性名 可以将属性声明为
16、读写属性、只读属性或只写属性。1. 声明读写属性修改 student 类定义添加属性声明:class student /类名为 student /声明字段精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 5 页,共 25 页6 / 25 private string id。private string name 。private bool sex。/声明属性public string ID get return id 。 set id=value 。 public string Name get return name。 set name=value
17、。 public bool Sex get return sex。 set sex=value。 在属性声明中,get 与 set 叫属性访问器。get 完成对数据值的读取,return 用于返回读取的值; set完成对数据值的设置修改,value 是一个关键字,表示要写入数据成员的值。属性名应和其要访问的数据成员名相关但不相同,可以采取数据成员名全用小写,而属性名的单词首字母大写的方式,如数据成员名为name,则对应的属性名为Name。精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 6 页,共 25 页7 / 25 2. 声明只读或只写属性在属性
18、声明中,如果只有get 访问器,则该属性为只读属性。例如:public bool Sex get return sex。 只读属性意味着,数据成员的值是不能被修改的。在属性声明中如果只有set 访问器,则该属性为只写属性。只写属性在程序设计中不常使用。4.2.1 使用属性属性成员的使用就如同公有数据成员的使用一样。可以为可写的属性赋值,可以用可读的属性为其他变量赋值。以 student 类为例:student S1=new student( ) 。/用属性设置修改数据成员值S1.ID=12345 。S1.Name= 张三 。S1.Sex=true。/用属性读取数据成员值为其他变量赋值bool
19、SSex=S1.Sex 。如果属性为只读的,则属性不能出现在赋值运算符的左边。在C#程序设计中,窗体与控件的属性就是这类成员,其中在属性窗口显示的属性,均为可读写属性,窗体与控件的只读属性只能在代码中使用。4.3 方法方法是把一些相关的语句组织在一起,用于解决某一特定问题的语句块。方法必须放在类定义中。方法同样遵循先声明后使用的规则。4.3.1 声明与调用方法方法的使用分声明与调用两个环节。1. 声明方法声明方法最常用的格式:访问修饰符返回类型方法名 (参数列表 ) 方法的访问修饰符通常是public ,以保证在类定义外部能够调用该方法。方法的返回类型用于指定由该方法计算和返回的值的类型,可以
20、是任何值类型或引用类型数精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 7 页,共 25 页8 / 25 据,如,int、string及前面定义的student类。如果方法不返回一个值,则它的返回类型为void。方法名是一个合法的C#标识符。参数列表在一对圆括号中,指定调用该方法时需要使用的参数个数、各个参数的类型,参数之间以逗号分隔。参数可以是任何类型的变量。如果方法在调用时不需要参数,则不用指定参数,但圆括号不能省。实现特定功能的语句块放在一对大括号中,叫方法体,“”表示方法体的开始,“ ”表示方法体的结束。如果方法有返回值,则方法体中必须包含
21、一个return 语句,以指定返回值,该值可以是变量、常量、表达式,其类型必须和方法的返回类型相同。如果方法无返回值,在方法体中可以不包含return 语句,或包含一个不指定任何值的return 语句。例如下面的方法声明:public int IntMax(int a,int b) int c。if(ab) c=a。else c=b。return c。 该方法的功能是求两个整数中较大的整数,并将该整数返回。该方法的返回类型是一个整型值,方法名称为“IntMax ”,参数列表中有两个整型变量参数“a”与“ b”,方法体中有一个return 语句,该语句指定的返回值是一个整型变量c。该方法体中的语
22、句块也可以用条件表达式合并为一句:“return ab?a:b。”。这时,return 指定的返回值是一个条件表达式, return 语句把该表达式运算的结果返回。2. 调用方法从方法被调用的位置,可以分为在方法声明的类定义中调用该方法和在方法声明的类定义外部调用方法。在方法声明的类定义中调用该方法的格式为:方法名 (参数列表 )在方法声明的类定义中调用该方法,实际上是由类定义内部的其他方法成员调用该方法。例如在类定义内部调用求较大整数函数:class CLASSMAX public int GetMax(int a,int b) /其他方法成员 return IntMax(a,b) 。 /在
23、类定义 CLASSMAX内部调用方法IntMax 精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 8 页,共 25 页9 / 25 public int IntMax(int a,int b) /求较大整数函数 return ab?a:b。 在方法声明的类定义外部调用该方法实际上是通过类声明的对象调用该方法,其格式为:对象名 .方法名 (参数列表 ) 【例4-1】创建一个控制台应用程序,并创建一个类,该类仅包含求两个数中较大数的方法成员,在Main 方法中调用该方法。using System。class Class1 class CLASSMAX
24、/定义一个类 public int IntMax(int a, int b) return ab?a:b。 static void Main(string args) int X=42,Y 。Y=100 。CLASSMAX classmax=new CLASSMAX()。 /声明一个对象int C=classmax.IntMax(X,Y)。 / 调用对象方法Console.Write( 较大的值为: ) 。Console.WriteLine(C) 。Console.ReadLine() 。 程序运行的结果为:较大的值为: 100 4.3.2 参数传递在方法的声明与调用中,经常涉及方法参数,在方
25、法声明中使用的参数叫形式参数(形参),在调用方法中使用的参数叫实际参数(实参)。在调用方法时,参数传递就是将实参传递给形参的过程。以【例 4-1】为例,声明方法时的形参如下:精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 9 页,共 25 页10 / 25 public int IntMax(int a,int b) 调用方法时的实参如下:classmax.IntMax( X,Y) 这样就完成了形参与实参的结合,其传递关系如图4-3 所示。方法调用: classmax.IntMax(X, Y) 方法声明: public int IntMax(int
26、 a, int b)图4-3 形参与实参方法参数传递按性质可分为按值传递与按引用传递。1. 按值传递参数按值的方式传递是指当把实参传递给形参时,是把实参的值复制(拷贝)给形参,实参和形参使用的是两个不同内存中的值,所以这种参数传递方式的特点是形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。按值传递如图4-4 所示。图4-4 按值传递示意基本类型的参数在传递时默认为按值传递。2. 按引用传递方法只能返回一个值,但实际应用中常常需要方法能够修改或返回多个值,这时只靠return 语句显然是无能为力的。如果需要方法返回多个值,使用按引用传递参数的方式可以实现这种效果。按引用传递是指实
27、参传递给形参时,不是将实参的值复制给形参,而是将实参的引用传递给形参,实参与形使用的是一个内存中的值。这种参数传递方式的特点是形参的值发生改变时,同时也改变实参的值。按引用传递分基本数据类型与类对象数据类型两种情况,其传递如图4-5 与图 4-6 所示。图4-5 基本类型按引用传递示意图4-6 类对象按引用传递示意基本类型参数按引用传递时,形参实际上是实参的别名。基本类型参数按引用传递时,实参与形参前均须使用关键字ref。【例4-2】编写一个控制台应用程序,在程序中添加一个交换两个整型变量值的方法。调用该方法将两个整型变量的值进行交换并输出交换前后的结果。using System。实参形参传递
28、实参形参传递实参形参传递精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 10 页,共 25 页11 / 25 namespace 数值交换 class Class1 public void Swap(ref int a,ref int b) /形参 a、b 为引用类型 / 数值交换int c=a。a=b。b=c。 static void Main(string args) int A=60,B=80 。Console.WriteLine(交换前 A、B 的值: 0 ,1,A,B)。Class1 c=new Class1( ) 。c.Swap(ref
29、 A,ref B) 。 / 以引用方式传递实参A、B Console.WriteLine(交换后 A、B 的值: 0 ,1,A,B)。Console.ReadLine( ) 。 程序运行结果:交换前 A、B 的值: 60,80 交换后 A、B 的值: 80,60 因为在Swap 方法中,对引用形参a、b 的值的修改,就是对实参A、B 的值的修改,所以方法 Swap 成功地完成了对A、B 数据的交换功能,相当于返回了两个值,这在按值传递的方式下是无法实现的。类对象参数总是按引用传递的,所以类对象参数传递不需要使用ref 关键字。类对象参数的传递,实际上是将实参对数据的引用复制给了形参,所以形参与
30、实参共同指向同一个内存区域。【例4-3】编写一个控制台应用程序,该程序工程名称为“类对象参数”,程序中有两个类定义,一个是创建程序时系统自动创建的类class1,一个是用户自定义的student 类。在class1 类中声明一个方法,该方法以student 类对象为参数。在class1 类中的Main 方法中调用该方法。using System。namespace 类对象参数 class student /类名为 student /声明字段private string id 。精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 11 页,共 25 页1
31、2 / 25 private string name。private bool sex 。/声明属性public string ID get return id 。 set id=value 。 public string Name get return name。 set name=value。 public bool Sex get return sex。 set sex=value。 class Class1 public void StudentF1(student ST1)/以对象 ST1 为形参 / 修改形参数据成员的值ST1.ID=56789 。ST1.Name= 张三 。精选学
32、习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 12 页,共 25 页13 / 25 ST1.Sex=true。 static void Main(string args) string SSex。student S1。 /声明一个学生类对象S1 S1=new student( ) 。 /为 S1 分配内存空间S1.ID=12345 。S1.Name= 李平 。S1.Sex=false。if (S1.Sex=true) SSex=男。else SSex=女。Console.WriteLine(学生学号 :+S1.ID+ 姓名 :+S1.Name + 性别
33、 :+SSex) 。 /输出对象 S1 的值Class1 c=new Class1( ) 。 /声明对象c以调用方法StudentF1 c.StudentF1(S1) 。/以对象 S1 为实参调用方法if (S1.Sex=true) SSex=男。else SSex=女。Console.WriteLine(学生学号 :+S1.ID+ 姓名 :+S1.Name + 性别 :+SSex) 。 /输出对象 S1 的值Console.ReadLine( ) 。 程序运行结果:学生学号: 12345 姓名:李平性别:女学生学号: 56789 姓名:张三性别:男结果的第一行为调用方法前对象S1 的值,结果
34、的第二行为调用方法后对象S1 的值。虽然在 Main 方法中并未对对象S1 进行修改,但由于调用方法StudentF1 时,将S1 传递给了ST1,而类对象参数是按引用传递的,所以在方法StudentF1 中对 ST1 的修改,就是对S1的修改,因此,结果的第二行,S1 的值发生了变化。4.3.3 重载方法有时候方法实现的功能需要针对多种类型的数据,虽然C#有隐式转换功能,但这种转换在有些情况下会导致运算结果的错误,而有时数据类型无法实现隐式转换甚至根本无法转换。例如:using System。class Class1 精选学习资料 - - - - - - - - - 名师归纳总结 - - -
35、 - - - -第 13 页,共 25 页14 / 25 public void Swap(ref int a,ref int b) int c=a。a=b。b=c。 static void Main(string args) int A=60,B=80 。Console.WriteLine( 交换前 A、B 的值: 0 ,1,A,B)。Class1 c=new Class1( ) 。c.Swap(ref A,ref B) 。 /调用交换值方法Console.WriteLine( 交换后 A、B 的值: 0 ,1,A,B)。Console.ReadLine( ) 。 上例中的交换方法只能实现两
36、个整型变量的值交换,无法通过隐式或显式转换来实现其他类型变量的值交换。例如在上例的主方法中添加如下代码:float fA=5.2f,fB=6.0f。c.Swap(ref fA,ref fB)。运行程序时,将出现“无法从ref float 转换为 ref int ”的编译错误。为了能使同一功能适用于各种类型的数据,C#提供了方法重载机制。方法重载是声明两个以上的同名方法,实现对不同数据类型的相同处理。方法重载有两点要求: (1) 重载的方法名称必须相同; (2) 重载的方法,其形参个数或类型必须不同,否则将出现“已经定义了一个具有相同类型参数的方法成员”的编译错误。如果要使上例中的交换方法能同时
37、处理整型与浮点型数据,重载的方法声明如下:public void Swap(ref int a,ref int b) public void Swap(ref float a,ref float b) 声明了重载方法后,当调用具有重载的方法时,系统会根据参数的类型或个数寻求最匹配的方法予以调用。根据前述的例子,当执行“c.Swap(ref A,ref B) 。”调用时,系统会调用“ public void Swap(ref int a,ref int b) ”方法,当执行“c.Swap(ref fA,ref fB) 。”调用时,系统会调用“public void Swap(ref float
38、a,ref float b) ”方法,从而实现对不同的数据类型进行相同处理。【例4-4 】创建一个控制台应用程序,在该程序中实现对两个整型、浮点型、双精度型、十进制型与字符型数据比较大小的功能。using System。namespace 方法重载 _比较值的大小 精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 14 页,共 25 页15 / 25 class Class1 /比较值大小的方法声明public int Max(int a, int b) return ab?a:b。 public float Max(float a, float b
39、) return ab?a:b。 public double Max(double a, double b) return ab?a:b。 public decimal Max(decimal a, decimal b) return ab?a:b。 public char Max(char a, char b) return ab?a:b。 static void Main(string args) int iA=60,iB=80。float fA=5.2f,fB=8.8f。double dA=6.2,dB=7.2 。decimal mA=8.2m,mB=9.8m。char cA=a,cB=
40、b 。Class1 c=new Class1( ) 。Console.WriteLine(iA与 iB 较大的值为:0,c.Max(iA,iB)。Console.WriteLine(fA与 fB 较大的值为: 0,c.Max(fA,fB)。Console.WriteLine(dA与 dB 较大的值为: 0,c.Max(dA,dB)。Console.WriteLine(mA与 mB 较大的值为: 0,c.Max(mA,mB)。Console.WriteLine(cA与 cB 较大的值为: 0,c.Max(cA,cB)。Console.ReadLine( ) 。 程序运行结果为:iA 与 iB 较
41、大的值为:80 精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 15 页,共 25 页16 / 25 fA与fB较大的值为:8.8 dA 与 dB 较大的值为:7.2 mA与mB较大的值为:9.8 cA 与 cB 较大的值为: b 在上面的程序例题中,由于float 类型可以隐式转换为double 类型,因此比较float 类型的值大小的方法可以省略。int 类型可以实现隐式转换,但由于int 类型既可以隐式转换为double 类型,也可以隐式转换为decimal 类型,所以,比较int 类型值大小的方法不能省略,以免产生调用的二义性。4.4 构造
42、函数构造函数是一种特殊的方法成员,构造函数的主要作用是在创建对象(声明对象)时初始化对象。一个类定义必须有至少一个构造函数,如果定义类时,没有声明构造函数,系统会提供一个默认的构造函数,如果声明了构造函数,系统将不再提供构默认造函数。如果只有默认构造函数,在创建对象时,系统将不同类型的数据成员初始化为相应的默认值,如,数值类型被初始化为0,字符类型被初始化为空格,字符串类型被初始化为null(空值),逻辑(bool)类型被初始化为false 等。例如下面的程序:using System。class student /类名为 student /声明字段public string id 。publ
43、ic string name 。public bool sex 。public int age 。 class Class1 static void Main(string args) char SSex。student S1=new student( ) 。if (S1.sex=true) SSex=男。else SSex=女。Console.WriteLine( 学号: +S1.id+ 姓名: +S1.name +性别: +SSex+ 年龄: 0,S1.age) 。Console.ReadLine( ) 。 精选学习资料 - - - - - - - - - 名师归纳总结 - - - - -
44、 - -第 16 页,共 25 页17 / 25 程序运行的结果为:学号:姓名:性别:女年龄:0 这是因为系统将student 类的数据成员id 与 name 初始化为null ,将 sex 初始为false,将age初始为 0 的缘故。如果想在创建对象时,将对象的数据成员初始为指定的值,则需要专门声明构造函数。4.4.1 声明构造函数声明构造函数与声明普通方法相比,有两个特别要求,一是构造函数不允许有返回类型包括 void 类型,二是构造函数的名称必须与类名相同。由于通常声明构造函数是为了在创建对象时,对数据成员初始化,所以构造函数往往需要使用形参,例如参考前面的学生(student)类,创
45、建一个学生类对象时,需要给出学生的学号、姓名、性别及年龄等,所以学生类构造函数可以声明如下:public student(string ID,string NAME,bool SEX,int AGE) id=ID 。name=NAME 。sex=SEX。age=AGE。 由于声明了上述带参数的构造函数,所以系统不再提供默认构造函数,这样在创建对象时,必须按照声明的构造函数的参数要求给出实际参数,否则将产生编译错误,例如:student S2=new student(12345, 张三 ,true,21) 。由上述创建对象的语句可知,new 关键字后面实际是对构造函数的调用。如果声明构造函数时使
46、用的参数名称与类数据成员名称相同,则构造函数中使用的类数据成员名称需要用关键字this 引导,以免系统分不清形参与数据成员而产生二义性。将上例中的形参名称改为与数据成员同名的构造函数声明如下:public student(string id,string name,bool sex,int age) this.id=id 。this.name=name。this.sex=sex。this.age=age。 关键字this 指所创建的对象,是声明对象时,由系统自动传递给构造函数的对象的引用形参。事实上在调用类的非静态方法成员时,系统均会自动传递该引用形参,不过最好的办法是使形参的名称与数据成员的
47、名称有所区别。4.4.2 重载构造函数构造函数与方法一样可以重载,重载构造函数的主要目的是为了给创建对象提供更大的精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 17 页,共 25 页18 / 25 灵活性,以满足创建对象时的不同需要。例如,在创建一个学生(student)对象时,有时可能需要只初始化姓名,而不初始化其他值,那么可以在前述声明构造函数的基础上再重载一个只初始化姓名的构造函数,代码如下:public student(string NAME) name=NAME 。 由于该构造函数与前述构造函数的参数个数不同,所以是一个合法的构造函数重
48、载。有了这个构造函数后,就可以声明只有一个实参的对象,例如:student S3=new student( 李平 )。如果在声明了带参数的构造函数后,还想保留默认构造函数,则必须显式声明一个默认构造函数,显式声明的默认构造函数实际上是一个不实现任何功能的空函数。以student 类为例,声明默认构造函数如下:public student( )/显式声明默认构造函数【例 4-5】根据本节介绍内容,在控制台应用程序中创建student 类,并声明构造函数及构造函数的重载。using System。namespace 构造函数 class student /类名为 student /声明字段publ
49、ic string id 。public string name 。public char sexx 。public bool sex 。public int age 。/声明构造函数public student( )/显式声明默认构造函数public student(string id,string name,bool sex,int age) / 重载初始化学号、姓名、性别与年龄的构造函数this.id=id 。this.name=name。this.sex=sex。this.age=age。 public student(string NAME)/重载只初始化姓名的构造函数 name=N
50、AME 。 class Class1 精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 18 页,共 25 页19 / 25 static void Main(string args) char SSex。/声明无参对象(调用默认构造函数)student S1=new student( ) 。if (S1.sex=true) SSex=男。else SSex=女。Console.WriteLine(学号: +S1.id+ ,姓名: +S1.name +,性别: +SSex+ ,年龄: 0,S1.age) 。/声明带学号、姓名、性别及年龄初始值的对象(
51、调用四参数构造函数)student S2=new student(12345, 张三 ,true,21) 。if (S2.sex=true) SSex=男。else SSex=女。Console.WriteLine(学号:+S2.id+,姓名:+S2.name +,性别: +SSex+ ,年龄: 0,S2.age) 。/声明带姓名初始值的对象(调用一参数构造函数)student S3=new student( 李平 ) 。if (S3.sex=true) SSex=男。else SSex=女。Console.WriteLine(学号: +S3.id+ ,姓名: +S3.name +,性别: +
52、SSex+ ,年龄: 0,S3.age) 。Console.ReadLine( ) 。 程序运行结果:学号:,姓名:,性别:女,年龄:0 学号: 12345,姓名:张三,性别:男,年龄:21 学号:,姓名:李平,性别:女,年龄:0 由于构造函数的重载,程序中可以用不同的初始化方式声明对象,为创建对象提供了灵活性。精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 19 页,共 25 页20 / 25 4.5 静态成员类可以具有静态成员,例如静态字段、静态方法等。4.5.1 静态数据成员与方法成员静态成员与非静态成员的不同在于,静态成员属于类,而非静态成
53、员则总是与特定的实例(对象)相联系。声明静态成员需要使用static 修饰符。1. 静态数据成员非静态的字段(数据)总是属于某个特定的对象,其值总是表示某个对象的值。例如,当说到学号(ID )时,总是指某个学生的学号,而不可能是全体学生的学号,相应的,在前面定义的student 类的 id 成员就是一个非静态的数据成员。有时可能会需要类中有一个数据成员来表示全体对象的共同特征,例如在student 类用一个数据成员来统计学生的人数,那么这个数据成员表示的就不是某个学生对象的特征,而是全体学生对象的特征,这时就需要使用静态数据成员。例如:class student /类名为 student pu
54、blic static int studentnumber 。 /声明静态数据成员public string id 。public string name 。public char sexx 。public bool sex 。public int age 。 静态数据成员studentnumber 不属于任何一个特定的对象,而是属于类,或者说属于全体对象,是被全体对象共享的数据。2. 静态方法非静态的方法包括非静态的构造函数总是对某个对象进行数据操作,例如,比较值的大小方法,总是某个对象的比较值大小的方法,相应的,在前述的例子中类Class1 中 Max 方法成员就是一个非静态的方法成员。如
55、果某个方法使用时并不需要与具体的对象相联系,例如方法操作的数据并不是某个具体对象的数据而是表示全体对象特征的数据,甚至方法操作的数据与对象数据根本无关,这时可以将该方法声明为静态方法。例如要操作前述student 类例子中的静态数据成员studentnumber,则应该声明一个静态方法。静态方法同样使用修饰符static 声明,静态方法属于类,只能使用类调用,不能使用对象调用。【例4-6】创建一个控制台应用程序,在该程序中定义一个学生类,该类除包含非静态成员外,还包含一个静态数据成员用以统计学生人数(对象个数),一个静态方法用以返回学生人数。精选学习资料 - - - - - - - - - 名
56、师归纳总结 - - - - - - -第 20 页,共 25 页21 / 25 using System。namespace 静态成员 class student /类名为 student /声明字段public static int studentnumber 。 /声明静态数据成员public string id 。public string name 。public char sexx 。public bool sex 。public int age 。public student(string id,string name,bool sex,int age) /声明构造函数 stude
57、ntnumber+ 。/ 每创建一个对象,静态数据成员增1this.id=id 。this.name=name。this.sex=sex。this.age=age。 public static int GetNumber( ) /声明静态方法 return studentnumber 。 class Class1 static void Main(string args) char SSex。student S1=new student(12345, 张三 ,true,21) 。 /声明对象 S1 if (S1.sex=true) SSex=男。else SSex=女。Console.Writ
58、eLine(学号: +S1.id+ ,姓名: +S1.name +,性别: +SSex+ ,年龄: 0,S1.age) 。student S2=new student(23456, 李四 ,false,20) 。 /声明对象S2 if (S2.sex=true) SSex=男。else SSex=女。Console.WriteLine(学号: +S2.id+ ,姓名: +S2.name 精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 21 页,共 25 页22 / 25 +,性别:+SSex+,年龄:0,S2.age)。student S3=new
59、 student(34567, 王五 ,true,19) 。/声明对象 S3 if (S3.sex=true) SSex=男。else SSex=女。Console.WriteLine(学号: +S3.id+ ,姓名: +S3.name +,性别: +SSex+ ,年龄: 0,S3.age) 。Console.WriteLine(学生人数为:0,student.GetNumber( )。 /调用静态方法Console.ReadLine( ) 。 程序运行结果:学号: 12345,姓名:张三,性别:男,年龄:21 学号: 23456,姓名:李四,性别:女,年龄:20 学号: 34567,姓名:王
60、五,性别:男,年龄:19 学生人数为: 3 该程序在构造函数被调用时,使静态数据成员studentnumber 自动增1,从而起到自动统计学生人数的作用,程序中的静态方法在调用时,是由类student 直接调用的,如果用对象调用则是非法的。静态方法与非静态方法一样可以重载。【例 4-7】修改【例4-4】,将比较值大小的方法重载声明为静态方法并在Main 方法中调用这些方法。using System。namespace 静态方法重载 class Class1 /比较值大小的重载静态方法声明public static int Max(int a, int b) return ab?a:b。 pub
61、lic static float Max(float a, float b) return ab?a:b。 public static double Max(double a, double b) return ab?a:b。精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 22 页,共 25 页23 / 25 public static decimal Max(decimal a, decimal b) return ab?a:b。 public static char Max(char a, char b) return ab?a:b。 stat
62、ic void Main(string args) int iA=60,iB=80。float fA=5.2f,fB=8.8f。double dA=6.2,dB=7.2 。decimal mA=8.2m,mB=9.8m。char cA=a,cB=b 。/调用重载静态方法Console.WriteLine(iA与 iB 较大的值为:0, Max (iA,iB) 。Console.WriteLine(fA与 fB 较大的值为: 0, Max (fA,fB) 。Console.WriteLine(dA与dB较大的值为:0,Max(dA,dB)。Console.WriteLine(mA与 mB 较大的
63、值为: 0, Max (mA,mB) 。Console.WriteLine(cA与 cB 较大的值为: 0, Max (cA,cB) 。Console.ReadLine( ) 。 程序运行结果同【例4-4】。在Class1 类定义中,比较值大小的方法并不是对类的数据成员进行操作,所以声明为静态方法更便于使用。由于静态方法的声明与调用均在Class1 类定义中,所以对静态方法的调用不需要使用类名引导。4.5.2 C#中的实用静态方法在 C#的 Windows 应用程序设计中,经常需要处理各种类型的数据,例如在程序运行时对文本框中输入的数值进行数学计算,但问题是,文本框中输入的数据是文本框的tex
64、t 属性,该属性是字符串类型,而字符串类型无法隐式或显式转换为数值型数据,因而也无法对其实现数学计算。在 C#的 System 命名空间中,有一个Convert (转换)类,该类提供了由字符串类型转换为相应其他基本数据类型的一系列静态方法,当然在由字符串类型转换为相应的基本数据类型时,字符串的字符序列必须符合相应数据类型的要求。类型转换方法最常用的调用格式之一是:Convert. 静态方法名 (字符串类型数据)精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 23 页,共 25 页24 / 25 下面介绍其中几种类型转换方法。1. Convert.T
65、oInt32方法该方法可以将字符串类型转换为int 类型。转换时,字符串必须符合整型数值的要求。例如:int A=Convert.ToInt32(123)。 /符合要求int B= Convert.ToInt32(123.6)。 /不符合要求,含有小数点,将引发转换异常int C=Convert.ToInt32(2147483648) /不符合要求,超出int 型的表示数值范围将字符串转换为整数类型的方法还有:ToInt16 方法,将字符串转换为short(短整型)类型;ToInt64方法,将字符串转换为long(长整型)类型;ToUInt32方法,将字符串转换为uint(无符号整型)类型等等
66、。2. Convert.ToSingle方法该方法可以将字符串类型转换为float (单精度浮点型)类型。例如:float A=Convert.ToSingle(123.5)。能将字符串类型转换为小数类型的方法还有:ToDouble 方法,将字符串转换为double(双精度浮点型)类型;ToDecimal 方法,将字符串转换为decimal (十进制型)类型。3. Convert.ToChar方法该方法将字符串转换为char(字符)类型。例如:char cD=Convert.ToChar(a)。 /将字符串常量 a 转换为字符类型相应的,其他类型的数据也可以使用ToString 方法被转换为字
67、符串类型,例如:string sA=Convert.ToString(1234)。将整型常量转换为字符串更简便的方法是直接在变量名后使用“.ToString( ) ”方法,例如:int A=1234 。string sA=A.ToString( )。 /将整型变量的值读取出来后转换为字符串需要注意的是这个ToString 方法与Convert.ToString方法的不同不仅在调用格式上,还在于这个 ToString 方法是在类object 中声明的虚方法(关于虚方法将在第5 章中介绍),它可以将所有类型的数据转换为字符串类型。4. ToBoolean 该方法可以将字符串转换为bool(布尔型)
68、类型。使用ToBoolean 方法时,字符串必须是true 或 false,例如:string sA=true 。bool bA=Convert.ToBoolean(sA)。掌握各种数据类型之间的转换方法,才能在C#程序设计中获得一定的自由,但是要获得更大的自由还要学会在程序设计中进行异常处理,关于异常处理的内容将中第13 章 异常处理与跟踪调试中介绍。【例 4-8】创建一个简单的Windows 应用程序,该程序完成在文本框中输入两个数值,单击相应的的按钮,输出两个数的和、差、积、商的运算,并在第三个文本框中输出计算结果,并通过标签对计算结果的性质作必要的说明。精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 24 页,共 25 页25 / 25 4.6 习题精选学习资料 - - - - - - - - - 名师归纳总结 - - - - - - -第 25 页,共 25 页