1、第五章面向对象程序设计面向对象程序设计 本章任务 能正确理解类与对象的概念 能使用方法 能正确在方法之间传递信息任务任务5.15.1计算长方形周长和面积计算长方形周长和面积任务描述:任务描述:输入长方形的长和宽 单击“计算”按钮,显示长方形面积和周长;单击“重置”按钮,清空文本框。处理“计算”、“重置”按钮的Click事件需要定义rectangle类实例化对象rec1分四步实现具体功能 任务任务5.15.1计算长方形周长和面积计算长方形周长和面积任务解决方案:任务解决方案:(1)创建名为circumArea1的Windows应用程序项目;(2)添加控件并设置属性。(3)编写程序代码(4)测试程
2、序 关键代码回顾 /创建对象 rectangle rec1=new rectangle();/输入长存入字段中 rec1.length=double.Parse(txtLength.Text);/输入的宽存入字段中 rec1.width=double.Parse(txtWidth.Text);/计算面积 double area=rec1.length*rec1.width;/计算周长 double perimeter=2*(rec1.length+rec1.width);类和对象类和对象 类:相对对象而言,类似于蓝图 包含方法和数据 对象:是类的实例 使用关键字 new 创建 对象具有行为对象
3、对象厨房厨房起居室起居室浴室浴室书房书房饭厅饭厅储藏室储藏室门廊门廊类类课堂讨论 先有对象还是先有类?类是对象的模板,使用对象前必须先创建类类是对象的模板,使用对象前必须先创建类定义类访问修饰符访问修饰符 class 类名类名 访问修饰符访问修饰符常量声明;常量声明;访问修饰符访问修饰符字段声明;字段声明;访问修饰符访问修饰符方法声明;方法声明;访问修饰符访问修饰符属性声明;属性声明;访问修饰符访问修饰符构造函数声明;构造函数声明;访问修饰符访问修饰符析构函数声明;析构函数声明;说明:说明:使用类声明可以创建新的类。类声明以一个声明头开始,其组使用类声明可以创建新的类。类声明以一个声明头开始,
4、其组成方式如下:先是指定类的特性或者修饰符,后跟类的名字,成方式如下:先是指定类的特性或者修饰符,后跟类的名字,它由一组包含在大括号(它由一组包含在大括号()中的成员声明组成)中的成员声明组成。例如例如:定义:定义rectangle 类:类:public class rectangle /定义类定义类 public double length;/定义字段定义字段 public double width;/定义字段定义字段 类的修饰符类的修饰符public(公有类):表示外界可以不受限制地对该类访问;private(私有类):表示只有本类才能访问;new(新建类):表示类由基类中继承而来、与基类
5、中同名的成员;protected(保护类):表示只能对其所在类和从该类派生的子类进行访问;internal(内部类):表示仅能访问本程序;abstract(抽象类):说明该类可以用来做其他类的基类,可以继承但不能单独使用;Scaled(密封类):说明该类不能作为其他类的基类,不能派生新的类;省略类修饰符则表示为private。1将类实例化为对象对象对象2类成员的访问 =new();例如:例如:rectangle rec1=new rectangle();对象名对象名.数据数据例如:例如:rec1.length;对类名推荐使用Pascal命名规范:每个单词的首字母要大写。创建并初始化对象属性属性
6、 访问修饰符访问修饰符 class ;=new();总结定义类类成员 在类的定义中,包含有各种类成员,概括起来类的成员有在类的定义中,包含有各种类成员,概括起来类的成员有两种:存储数据的成员与操作数据的成员。存储数据的成两种:存储数据的成员与操作数据的成员。存储数据的成员叫员叫“字段字段”,操作数据成员又有很多种。,操作数据成员又有很多种。“字段字段”是类定义中的数据,也叫类的变量。在是类定义中的数据,也叫类的变量。在C#C#中,字中,字段以图标段以图标 表示。表示。“属性属性”用于读取和写入用于读取和写入“字段字段”值。在值。在C#C#中,属性以图中,属性以图标标 表示表示 。“方法方法”实
7、质上就是函数,通常用于对字段进行计算和操实质上就是函数,通常用于对字段进行计算和操作,即对类中的数据进行操作,以实现特定的功能。在作,即对类中的数据进行操作,以实现特定的功能。在C#C#中,方法以图标表示,事件方法以图标中,方法以图标表示,事件方法以图标 表示。表示。“构造函数构造函数”是在用类声明对象时,完成对象字段的初始是在用类声明对象时,完成对象字段的初始化工作。化工作。字段为类中的成员,是表示与对象或类关联的变量,字段常定义的类型有public(公有型)、private(私有型)等。字段例如:例如:class triangle /定义类定义类public string name;/字段
8、成员字段成员public double sideA;/字段成员字段成员public double sideB;/字段成员字段成员public double sideC;/字段成员字段成员 加上static保留字为静态成员,没有这个保留字的为非静态成员。区别:静态成员属于类所有,非静态成员属于类的对象所有,因此访问静态成员只能由类来访问,而访问非静态成员必须由对象进行访问。静态字段静态字段的访问格式静态字段的访问格式:类名类名.静态成员名静态成员名非静态字段的访问格式:对象名非静态字段的访问格式:对象名.非静态成员名非静态成员名class Teacher public string number
9、=1001;/非静态字段 public string name=张宁;/非静态字段 public static double pay=3650.5;/静态字段static void Main(string args)Teacher t1=new Teacher();string str=职工编号:+t1.number+,姓名:+t1.name+,工资:+Teacher.pay.ToString();Console.WriteLine(str);例如:number和name为非静态字段,pay为静态字段,因此访问前面两个字段用对象调用t1.number和t1.name,访问最后一个字段用类来调用
10、Teacher.pay。在任务5.1的基础上自定义方法Area()计算长方形面积 自定义方法Perimeter()计算长方形周长 修改“计算”按钮的Click事件任务任务5.2 5.2 定义方法计算长方形周长和面积定义方法计算长方形周长和面积代码代码/自定义方法Area()、Perimeter()public double Area()/定义计算长方形面积的方法 double a=length*width;return a;public double Perimeter()/定义计算长方形周长的方法 double len=2*(length+width);return len;返回面积返回面积
11、代码代码/修改“计算”按钮,在其Click事件处理程序 private void btnCalculate_Click(object sender,EventArgs e)/调用recArea方法计算面积 double s=rec1.Area();/调用recPerimeter方法计算周长 double l=rec1.Perimeter();lblResult.Text=长方形面积为:+s.ToString()+平方厘米,+周长为:+l.ToString()+厘米;类的方法表示一类事物所具备的动作,在C#中就是类或对象为完成一项任务而执行的指令序列。在编写程序中使用方法主要是便于修改、增加可读
12、性,方法可以重用和封装。方法方法 编写方法 编写方法前的准备工作:该方法主要是实现什么功能?根据功能起一个容易记的方法名。实现该方法需要用户提供什么数据?确定方法的参数。如计算n!,就需要给该方法传入一个整数n。该方法是实现操作或是计算?若是操作,方法的返回类型可以定为void 若是计算,需要确定需要给调用者返回一个什么类型的结果。如求n!需要给调用者返回一个长整型的阶乘值。方法的最后一个执行语句应当是:(若是操作可以省略)return 表达式;编写方法 编写方法前的准备工作:该方法可以给哪些人使用:public:所有人都可以使用 private:只能给本类使用 protected:可以给本类
13、和派生类使用 方法要写在类(class)中。编写方法 方法定义的格式 方法修饰符方法修饰符 返回值类型方法名返回值类型方法名(形参列表形参列表)方法体方法体 l 方法修饰符是可选的。它可以是方法修饰符是可选的。它可以是public,private,protected 等之一。等之一。默认为默认为private。l 方法名要符合标识符的命名规则。方法名要符合标识符的命名规则。l 参数的声明和变量一样,即参数的声明和变量一样,即 类型名类型名 参数名参数名 的格式,用于接收调的格式,用于接收调用者传过来的数值,也相当于方法的局部变量。多个参数之间用用者传过来的数值,也相当于方法的局部变量。多个参数
14、之间用逗号分隔。逗号分隔。任务任务 5.3 5.3 根据圆的半径计算根据圆的半径计算圆面积圆面积任务描述:任务描述:输入圆半径 单击“计算”按钮,显示圆面积。需要定义Circle类自定义方法Area()需要根据参数r计算面积任务任务 5.3 5.3 根据圆的半径计算根据圆的半径计算圆面积圆面积代码代码/定义Circle类 public class Circle public const double PI=3.14159;public double r;public double Area(double r)this.r=r;/this.r表示类的字段,r表示方法Area的形参 double
15、s=PI*this.r*this.r;return s;返回面积返回面积传入半径传入半径代码代码/修改“计算”按钮,在其Click事件处理程序 private void btnCompute_Click(object sender,EventArgs e)Circle circle1=new Circle();double r=double.Parse(txtR.Text );/将实参r中的值传递给方法area中的形参r double s=circle1.Area(r);string res=园半径是:+circle1.r+厘米,面积是:+s.ToString()+平方厘米;txtResult
16、.Text=res;方法参数按值传递 public double Area(double r)double s=double s=circle1.Area(r);circle1.Area(r);方法的声明方法的调用方法的调用调用者调用者area方法方法11.511.5使用以下代码,调用上述方法,结果是什么?使用以下代码,调用上述方法,结果是什么?public double Area(double r,double s)this.r=r;s=PI*this.r*this.r;return s;double a=0;Circle circle1=new Circle();double r=doub
17、le.Parse(txtR.Text );double s=circle1.Area(r,a);string res=园半径是:+circle1.r+厘米,面积是:+s.ToString()+平方厘米,+a的值是:+a;txtResult.Text=res;方法参数按值传递使用以下代码,调用上述方法,结果是什么?使用以下代码,调用上述方法,结果是什么?public double Area(double r,ref ref double s)this.r=r;s=PI*this.r*this.r;return s;double a=0;Circle circle1=new Circle();do
18、uble r=double.Parse(txtR.Text );double s=circle1.Area(r,refref a);string res=园半径是:+circle1.r+厘米,面积是:+s.ToString()+平方厘米,+a的值是:+a;txtResult.Text=res;通过引用传递参数public double area(double r,public double area(double r,outout double s)double s)this.r=r;this.r=r;s=PI s=PI*this.r this.r*this.r;this.r;return s
19、;return s;Circle circle1=new Circle();Circle circle1=new Circle();double r=double.Parse(txtR.Text );double r=double.Parse(txtR.Text );double s=circle1.area(r,double s=circle1.area(r,outout a);a);string res=string res=园园半半径径是:是:+circle1.r+circle1.r+厘米,面厘米,面积积是:是:+s.ToString()+s.ToString()+平方厘米,平方厘米,+
20、a+a的的值值是:是:+a;+a;通过引用传递参数 编译器通过识别方法的签名来区分调用哪个方法。方法的签名包括方法名、参数的类型、个数和顺序 对以下重载的方法,编译器能够正确识别:对以下方法,编译器无法识别,会报错:方法的重载public void Foo(int a,float b)public void Foo(float a,int b)public void Foo(int a,int b)public void Foo(int b,int a)例如:例如:class Compute public void print(int i)int s=i*i;Console.WriteLine
21、(s);return;public void print(int i,int j)int s=i*j;Console.WriteLine(s);return;方法的重载任务任务 5.4 5.4 求三角形周长和面积求三角形周长和面积 任务描述:任务描述:输入输入三角形的三条边长度 判断能否构成三角形 单击“计算”按钮,显示三角形 周长和面积类、成员访问规则:命名空间总是默认的public类总是默认的public类成员默认为private对于一个类成员只能使用一个访问修饰符。private internal尽管是两个单词,但它是单个访问修饰符。成员的作用域永远不超出包含它的类。任务任务 5.4 5
22、.4 求三角形周长和面积求三角形周长和面积定义Triangle 类,并且将字段成员sideA、sideB、sideC定义成私有类型 自定义方法Setside()和Getside(),来存取字段自定义方法test(),测试任意两条边之和是否大于第三条边 自定义方法triCir()和triArea(),计算三角形的周长和面积。任务任务 5.4 5.4 求三角形周长和面积求三角形周长和面积代码代码/定义Setside()方法 public void Setside(double paraA,double paraB,double paraC)this.sideA=paraA;this.sideB=p
23、araB;this.sideC=paraC;/定义Getside()方法 public string Getside()string txt=三角形三边长分别为:+sideA+厘米、+sideB+厘米和+sideC+厘米。;return txt;无返回无返回传入三边长传入三边长代码代码/定义test()方法测试能否构成三角形 public bool test()if(sideA+sideB sideC)&(sideB+sideC sideA)&(sideA+sideC sideC)return true;else return false;课堂讨论 这样的设计对于使用者来说,不方便了。因为他要
24、分别记住2个方法,哪个方法是设置值的,哪个方法是获取值的。有没有一种途径,它可以让使用者像使用公共的字段一样方便,又能让设计者能够进行安全地检查?使用属性使用属性任务任务 5.5 5.5 使用属性求三角形周长和使用属性求三角形周长和面积面积 任务描述:任务描述:输入输入三角形的三条边长度 判断能否构成三角形 单击“计算”按钮,显示三角形 周长和面积修改任务5.4定义属性来存取字段任务任务 5.5 5.5 使用属性求三角形周长和使用属性求三角形周长和面积面积 属性属性,从外部属性,从外部(类的使用者类的使用者)来看,类似于类的字段。从内部来看,类似于类的字段。从内部(类的作者类的作者)来看,类似
25、来看,类似于类的方法。于类的方法。class Triangle public double SideA /定义SideA属性 get return sideA;set sideA=value;get访问器访问器set访问器访问器 属性是一种用于访问类或对象性质的成员,它与字段不同的是属性不表示存储位置,不直接操作类的数据内容,而是通过访问器进行访问。使用属性可以像使用公共数据成员一样,这样既可以使类中数据被轻松访问,又可以避免随意访问类中公共数据而出现的安全性问题,提高了程序安全可靠性,充分体现了对象的封装性。属性属性 如果只提供 get访问器,那么这个属性是只读的。如果只提供 set访问器,
26、那么这个属性是只写的。建议:将所有字段都定义为private。如果字段需要公开,请使用属性将其公开。属性 属性访问方法与类的字段成员访问方法一样,根据属性声明时是否为静态有下列两种访问形式。非静态属性成员访问:对象名.属性名 静态属性成员访问:类名.属性名属性使用属性使用属性使用属性使用 /创建对象 Triangle tri1=new Triangle();s1=double.Parse(txtSideA.Text);s2=double.Parse(txtSideB.Text);s3=double.Parse(txtSideC.Text);/使用属性set访问器写入数据 tri1.SideA=
27、s1;tri1.SideB=s2;tri1.SideC=s3;/使用属性get访问器读取数据 txtRes.Text=三角形三边长分别为:+tri1.SideA+厘米、+tri1.SideB+厘米和+tri1.SideC+厘米。;任务任务5.6 5.6 使用构造函数初始化三角形使用构造函数初始化三角形边长边长任务描述:任务描述:使用构造函数初始化三角形边长使用构造函数初始化三角形边长修改任务5.5中定义的Triangle类,添加构造函数构造函数用以完成初始化用以完成初始化 任务任务5.6 5.6 使用构造函数初始化三角形使用构造函数初始化三角形边长边长代码代码 class Triangle p
28、ublic Triangle()/系统默认构造函数 public Triangle(double sA)/构造函数 sideA=sideB=sideC=sA;没有返回值没有返回值与类同名与类同名 构造函数是特殊的方法,用来实现对象的初始化 与类同名 没有返回值 构造函数用于,当实例化一个对象时,会对类的成员进行初始化。构造函数如果我们想使对象初始化时,可以按照设计者的预期来进行,就需要自己书写构造函数的代码。若上述任务5.6中的Triangle类,我们想初始化的三边长 是0,构造函数可以定义为:class Triangle public Triangle(double sA)/构造函数 sid
29、eA=sideB=sideC=sA;Triangle tri1=new Triangle(0);构造函数的定义 class Triangle private double sideA;private double sideB;private double sideC;public Triangle()/系统默认构造函数 public Triangle(double sA)/构造函数 sideA=sideB=sideC=sA;构造函数的重载当没有构造函数时,.NET会提供一个默认空的构造函数。如任务5.6中的Triangle类,会有一个默认构造函数:public Triangle()/系统默认构
30、造函数 编译器只会当类中没有构造函数时,提供一个默认构造函数。但是若提供了其它版本的构造函数,而没有提供默认构造函数,将不能调用默认构造函数。构造函数的重载 例如在任务5.6中没有定义 public Triangle()就不能这样去实例化对象:Triangle tri1=new Triangle();构造函数的重载析构函数析构函数也是类的特殊方法,主要用于释放类的实例。析构函数的名字与类名相同,但它的前面需要加一个“”符号;与构造函数不同的是析构函数不能带参数,也没有返回值;当撤消对象时,自动调用析构函数;析构函数不能被继承,也不能被重载。析构函数声明格式:类名称()语句块 例如:class
31、Round public Round()/构造函数 public Round(int r)this.r=r;/构造函数重载 Round()/析构函数 Console.WriteLine(Round()is release);任务任务5.7 5.7 计算圆面积和周长、圆柱计算圆面积和周长、圆柱体面体面积和体积积和体积 任务描述:任务描述:设计计算器界面 通过多级继承,完成圆面积和周长的计算、圆柱体面积和体积的计算。将圆心坐标定义为基类,在基类中包含坐标X属性和Y属性,定义Print方法用来输出圆心坐标位置 继承基类建立圆派生类,新增圆的半径R属性,定义求圆面积和周长的方法,并重写Print方法用
32、来输出圆心坐标、圆面积和周长 将圆作为基类建立圆柱派生类,新增圆柱体的高度Height这一属性,重写求面积方法,改为求圆柱表面积,定义求体积的方法,重写Print方法用来输出圆柱体表面积和体积等 任务任务 5.3 5.3 根据圆的半径计算根据圆的半径计算圆面积圆面积定义基类定义基类 public class Centre protected int x,y;public Centre()public Centre(int myx,int myy)x=myx;y=myy;public virtual string Print()string str=圆心坐标:圆心坐标:(+X.ToString(
33、)+,+Y.ToString()+);return str;构造函数构造函数虚拟方法虚拟方法 定义派生类定义派生类 public class Round:Centre /定义派生类定义派生类 private int r;public Round()public Round(int myx,int myy,int myr):base(myx,myy)r=myr;public override string Print()string str=base.Print()+rn+圆的面积:圆的面积:+Area().ToString()+rn+圆的周长:圆的周长:+Perimeter().ToStrin
34、g();return str;方法覆盖方法覆盖从派生类中访问基类的属性和从派生类中访问基类的属性和方法成员方法成员 面向对象编程的重要特性有:封装、继承和多态。继承性是指在类之间建立一种关系,即在基类的基础上建立新的类称为派生类,使得新定义的派生类可以继承基类的成员,还可以定义新的成员,建立起类的新层次。多态性是指在类中定义了功能不同但名称相同的方法或属性,通过在类中声明虚拟方法和属性,在派生类中重写这些方法成员的实现,使得调用名称相同的方法成员所执行的操作可能不同,展示出类的多态性。继承与多态派生类派生类声明格式:Class 派生类名:基类名语句块1派生类与基类派生类是从基类中继承成员,并且
35、添加新成员或重新定义成员,一般称被继承的类为基类或父类,继承后产生的新类称为派生类或子类。基类中构造函数和析构函数不能被派生类继承,其它成员都能被继承。派生类2基类成员修饰符基类成员修饰符可为public、protected、private其含义如下:public:访问该成员不受任何限制,可以被基类和派生类访问,也可以被外部代码访问。protected:访问该成员仅限于派生类,基类和外部代码都不能访问。private:该成员仅限于包含它的类,不能被派生类和外部代码访问。派生类3Base关键字的作用。Base关键字是用于从派生类中访问基类的成员。有两种基本用法:(1)指定创建派生类实例时应调用的
36、基类构造函数,在派生类中显式调用直接基类构造函数,完成对基类成员的初始化工作;public User(string myid,string name,string sex,string myunit,string mypurview,string mytel):base(name,sex)id=myid;unit=myunit;purview=mypurview;tel=mytel;派生类(2)在派生类中访问基类成员。Base.方法名或Base.属性名例如:在项目任务5.7的User派生类中用base调用Print方法。public new string Print()string str2=
37、职工号:+id+base.Print()+所在单位:+unit+初始密码:+pwd+用户权限:+purview+联系电话:+tel;return str2;派生类4new修饰符的作用。若派生类中的成员和基类的成员使用了相同名称、相同参数和类型时,系统编译时会发出警告,但不会发生错误。这时可以用new修饰符显式地隐藏从基类继承的成员。虚拟方法和重写方法1虚拟方法派生类继承了基类方法,如果在派生类中需要用到与基类相同方法名,但方法执行的操作内容不同时,则需要“重写”它们,即重写方法成员的实现。在基类中声明方法时用virtual关键字来修饰,表示此方法为虚拟方法,用virtual声明的方法可以在派生
38、类中重写。虚拟方法声明中不能包含static、abstract、override修饰符。在声明方法时没有用virtual关键字的,该方法为非虚拟方法。非虚拟方法的执行是不变的,不管方法在它声明的类的实例还是在派生类的实例中被调用,执行都是相同的。虚拟方法和重写方法2重写方法重写方法就是在派生类中用相同的名称重写基类继承的虚拟方法,也就是在派生类中重新定义此虚拟方法的实现,在声明方法时用override关键字来修饰,表示该方法为重写方法。虚拟方法的执行可以被派生类重写后改变,具体实现时要求重写方法的方法名、返回值类型、参数表中的参数个数、类型顺序都必须与基类中的虚拟方法完全一致。在方法声明中不能
39、有new、static、virtual修饰符。3多态性 抽象类和抽象成员1抽象类在类声明中用abstract关键字进行修饰,表明此类只能是其他类的基类,称为抽象类。在类中含有一个或一个以上的抽象成员的类必须声明为抽象类,抽象类也可以包含非抽象类成员,当类中一个抽象类成员都没有的情况下,也可以将该类声明为抽象类。抽象类是为了提供一个比较合适的类,由此派生出其他的类,它不能实例化,而是在派生类中实现。2抽象成员抽象类中包含的抽象成员可以是抽象属性和抽象方法,声明时都用abstract关键字进行修饰,表示该属性和方法未实现,其中抽象方法没有方法体,但必须声明,在它的派生类中通过重写提供实现。谢 谢!