
面向对象程序设计思想下.ppt
32页计算机语言C#第三章 面向对象程序设计思想(下) 上一讲作业答案•1、 C#语言中,值类型和引用类型有何不同?•【解答】–值类型和引用类型的区别在于,值类型的变量直接存放实际的数据,而引用类型的变量存放的则是数据的地址,即对象的引用–值类型变量直接把变量的值保存在堆栈中,引用类型的变量把实际数据的地址保存在堆栈中,而实际数据则保存在堆中注意,堆和堆栈是两个不同的概念,在内存中的存储位置也不相同,堆一般用于存储可变长度的数据,如字符串类型;而堆栈则用于存储固定长度的数据,如整型类型的数据int(每个int变量占用四个字节)由数据存储的位置可以得知,当把一个值变量赋给另一个值变量时,会在堆栈中保存两个完全相同的值;而把一个引用变量赋给另一个引用变量,则会在堆栈中保存对同一个堆位置的两个引用,即在堆栈中保存的是同一个堆的地址在进行数据操作时,对于值类型,由于每个变量都有自己的值,因此对一个变量的操作不会影响到其它变量;对于引用类型的变量,对一个变量的数据进行操作就是对这个变量在堆中的数据进行操作,如果两个引用类型的变量引用同一个对象,实际含义就是它们在堆栈中保存的堆的地址相同,因此对一个变量的操作就会影响到引用同一个对象的另一个变量。
•2、C#中不同整型之间进行转换的原则是什么?•【解答】–在整型之间进行转换时,小范围类型可以隐式转换为大范围类型,但大范围类型转换为小范围类型时需要使用显式转换计时工HourlyWorker• class HourlyWorker• {• string name;• int age;• double wagePerHour;• double workHours;• public void setWagePerHour(double wagePerHour)• {• this.wagePerHour = wagePerHour;• }• public void setWorkHours(double workHours)• {• this.workHours = workHours;• }• public double earnings()• {• return this.wagePerHour * this.workHours;• }• public string getName()• {• return this.name;• }• public int getAge()• {• return this.age;• }• public void setName(string name)• {• this.name = name;• }• public void setAge(int age)• {• this.age = age;• }• public void setValue(string name, int age, double wagePerHour, double workHours)• {• this.setName(name);• this.setAge(age);• this.setWagePerHour(wagePerHour);• this.setWorkHours(workHours);• }• public string show()• {• return this.name + "(" + this.age + "):" + this.earnings();• }• }计件工PieceWorker• class PieceWorker• {• string name;• int age;• int quantity;• double price;• public void setQuantity(int quantity)• {• this.quantity = quantity;• }• public void setPrice(double price)• {• this.price = price;• }• public double earnings()• {• return this.quantity * this.price;• }• public string getName()• {• return this.name;• }• public int getAge()• {• return this.age;• }• public void setName(string name)• {• this.name = name;• }• public void setAge(int age)• {• this.age = age;• }• public void setValue(string name, int age, int quantity, double price)• {• this.setName(name);• this.setAge(age);• this.setQuantity(quantity);• this.setPrice(price);• }• public string show()• {• return this.name + "(" + this.age + "):" + this.earnings();• }• }合同工ContractWorker• class ContractWorker• {• string name;• int age;• double salary;• int quantity;• double price;• public void setSalary(double salary)• {• this.salary = salary;• }• public void setQuantity(int quantity)• {• this.quantity = quantity;• }• public void setPrice(double price)• {• this.price = price;• }• public double earnings()• {• return this.salary + this.quantity * this.price;• }• public string getName()• {• return this.name;• }• public int getAge()• {• return this.age;• }• public void setName(string name)• {• this.name = name;• }• public void setAge(int age)• {• this.age = age;• }• public void setValue(string name, int age, double salary,int quantity, double price)• {• this.setName(name);• this.setAge(age);• this.setSalary(salary);• this.setQuantity(quantity);• this.setPrice(price);• }• public string show()• {• return this.name + "(" + this.age + "):" + this.earnings();• }• }测试程序• class Program• {• static void Main(string[] args)• {• HourlyWorker hw = new HourlyWorker();• PieceWorker pw = new PieceWorker();• ContractWorker cw = new ContractWorker();• hw.setValue("Tom", 32, 8, 40);• Console.WriteLine(hw.show());• pw.setValue("Mike", 44, 3, 800);• Console.WriteLine(pw.show());• cw.setValue("Lee", 35, 1000, 2, 800);• Console.WriteLine(cw.show());• Console.ReadLine();• }• }•输出:•Tom(32):320•Mike(44):240•Lee(35):2600问题:•1、合同工的底薪是相同。
如果有100个合同工,他们的底薪原来是1000,那么我们要为这100个对象设置底薪有一天老板突发善心将底薪提高为1200元,我们要重新设置这100个对象的底薪这将是非常头疼,而且易错的事•2、每个字段的访问控制比较繁琐•3、在测试程序中使用setValue设置参数仍稍嫌麻烦,做好在创建对象是直接设置•4、三个类中有大量代码相同,应该提高代码复用学习目标•1、理解类成员和实例成员•2、理解字段和属性的差别•3、理解构造方法的作用•4、理解方法重载一、解决问题1•C#中将类的属性和方法分为两类:•一类为该类所有对象所共享,称之为“类成员”,包括类属性和类方法例如底薪salary •另一类是每个对象独自存储,互不干扰的属性和方法,称为实例成员例如姓名name1.1类属性•类属性在定义时使用static关键字,如:–class ContractWorker–{•static double salary;•……–}•类属性在使用时通过类名.属性名访问如:–ContractWorker. salary例1• class ContractWorker• {• string name;• int age;• public static double salary;• int quantity;• double price;• ……• public double earnings()• {• return ContractWorker.salary + this.quantity * this.price;• }• ……• }• class Program• {• static void Main(string[] args)• {• ContractWorker cw1 = new ContractWorker();• ContractWorker cw2 = new ContractWorker();• ContractWorker.salary = 1000;• cw1.setValue("Tom", 25, 2, 700);• cw2.setValue("Lee", 35, 2, 800);• Console.WriteLine(cw1.show());• Console.WriteLine(cw2.show());• ContractWorker.salary = 1200;• Console.WriteLine(cw1.show());• Console.WriteLine(cw2.show());• Console.ReadLine();• }• }1.2实例属性•使用实例属性时必须先创建对象(实例),然后通过对象.属性名访问。
1.3类方法•使用static声明的方法称之为类方法,或静态方法•使用类方法时通过类名.方法名调用类方法中不能使用实例属性和实例方法,否则编译错误例2• class Class1• {• static int i;• int j;• public void setJ(int j)• {• this.j = j;• }• public static int f1()• {• return i + j;//错误• }• public int f2()• {• return i + j;• }• }• class Program• {• static void Main(string[] args)• {• Class1 c = new Class1();• c.setJ(3);• Console.WriteLine(Class1.f1());• Console.WriteLine(c.f2());• Console.ReadLine();• }• }1.4实例方法•只有创建了对象才能调用的方法称为实例方法。
二、解决问题2•思路:•(1)每个属性设置一个访问权限,设置哪些程序可以访问他们•(2)根据属性的需要,分别设置为只读、只写、可读可写2.1访问修饰符•C#中常用的访问修饰符:–public (常用)任何外部的类都可以不受限制不受限制的存取这个类的方法和数据成员–internal 在当前项目中项目中都可以存取该访问权限一般用于基于组件的开发,因为它可以使组件以私有方式工作,而该项目外的其它代码无法访问–protected 除了让本身的类可以使用之外,任何继承自此类的子类子类都可以存取–protected internal 只限于当前项目项目,或者从该项目的类继承的子类子类才可以存取–private (常用)类中的所有方法和数据成员只能只能在此类中在此类中使用,外部无法存取默认)例3•如果将以前例子中方法的public去掉,程序将编译错误•因为此时默认为private,私有类型,测试程序和它不在同一类中,所以无法访问2.2get访问器•get访问器意味着可以对成员的值进行读操作•get访问器对应一个不带参数的方法,并返回一个与属性相同类型的值•步骤:–(1)定义一个私有的数据成员。
如:•private string name;–(2)定义一个共有属性,通过get方法获得私有成员的值如:•public string Name•{–get {return name;}•}2.3set访问器•set访问器意味着可以对成员的值进行写操作•set访问器将隐式参数value间接地向私有字段赋值•步骤:–(1)定义一个私有的数据成员如:•private sting name;–(2)定义一个共有属性,通过set方法设置私有成员的值如:•public string Name•{–get {return name;}–set {name=value;}•}•如果一个属性既包括get访问器又包括set访问器,那么这个属性为可读可写属性•如果一个属性只包括get访问器,那么这个属性为只读属性•如果一个属性只包括set访问器,那么这个属性为只写属性例4•class HourlyWorker• {• private string name;• public string Name• {• get { return name; }• set { name = value; }• }• private int age;• public int Age• {• get { return age; }• set { age = value;}• }• private static double wagePerHour;• public static double WagePerHour• {• get { return wagePerHour; }• set { HourlyWorker.wagePerHour = value; }• }• private double workHours;• public double WorkHours• {• get { return workHours; }• set{ workHours = value;}• }• • public double earnings()• {• return HourlyWorker.wagePerHour * this.workHours;• } • public void setValue(string name, int age, double workHours)• {• this.name = name;• this.age = age;• this.workHours = workHours;• }• public string show()• {• return this.name + "(" + this.age + "):" + this.earnings();• }• }• class Program• {• static void Main(string[] args)• {• HourlyWorker hw1 = new HourlyWorker();• HourlyWorker.WagePerHour = 8;• hw1.Name = "Tom";• hw1.Age = 32;• hw1.WorkHours = 40;• Console.WriteLine("{0}({1}):{2}", hw1.Name, hw1.Age, hw1.earnings());• HourlyWorker hw2 = new HourlyWorker();• hw2.setValue("Mike", 44, 40);• Console.WriteLine(hw2.show());• HourlyWorker.WagePerHour = 10;• • Console.WriteLine(hw1.show());• Console.WriteLine(hw2.show());• Console.ReadLine();• }• }三、解决问题3•当我们使用new创建对象时,程序首先为对象分配内存,然后调用构造方法初始化各成员。
•1、构造方法是与类名相同的方法,且无任何返回值(连void也没有)•2、使用构造方法的好处是它能够确保每一个对象在被使用之前都适当地进行了初始化的动作•3、如果程序中没有定义任何构造方法,系统将自动产生一个形如:public 类名(){}的构造方法,此构造方法称为“默认构造方法”•4、一个类可以有多个构造方法,只要他们的参数列表不同(参数类型、个数),此时称为方法重载•5、默认构造函数自动将非静态成员初始化:–数值型:如int、double等,初始化为0–bool类型:初始化为false.–引用类型:初始化为null•6、如果自己定义了构造函数,则不会自动进行初始化例5•在例4的中增加下列代码:• class HourlyWorker• {• ……• public HourlyWorker()• {• setValue("", 0, 0);• }• public HourlyWorker(string name, int age, double workHours)• {• setValue(name, age, workHours);• }•}• class Program• {• static void Main(string[] args)• {• HourlyWorker hw1 = new HourlyWorker("Mike", 44, 40);• HourlyWorker.WagePerHour = 8; • Console.WriteLine(hw1.show());• Console.ReadLine();• }• }四、方法参数传递•1、传递值类型的参数–using System;–using System.Collections.Generic;–using System.Text;–namespace ValueTransferExample–{– class Program– {– public static void AddOne(int a)– {– a++;– }– static void Main()– {– int a = 3;– Console.WriteLine("调用AddOne之前,a={0}", a);– AddOne(a);– Console.WriteLine("调用AddOne之后,a={0}", a);– Console.ReadLine();– }– }–}•2、传递引用类型的参数–using System;–using System.Collections.Generic;–using System.Text;–namespace ReferenceTransferExample–{– class Program– {– public static void AddOne(ref int a)– {– a++;– }– static void Main()– {– int x = 3;– Console.WriteLine("调用AddOne之前,x={0}", x);– AddOne(ref x);– Console.WriteLine("调用AddOne之后,x={0}", x);– Console.ReadLine();– }– }–}•3、输出多个引用类型的参数 •输出引用类型参数的格式为: out 参数类型 参数名–using System;–using System.Collections.Generic;–using System.Text;–namespace ReferenceOutExample–{– class Program– {– public static void MyMethod(out int a, out int b)– {– a = 5;– b = 6;– }– static void Main()– {– int x, y;– MyMethod(out x, out y);– Console.WriteLine("调用MyMethod之后,x={0},y={1}", x, y);– Console.ReadLine();– }– }–}•4、、传递个数不确定的参数传递个数不确定的参数 •需要传递的参数个数不确定时,可以采用需要传递的参数个数不确定时,可以采用params关键字。
关键字–using System;–using System.Collections.Generic;–using System.Text;–namespace UncertaintyTransferExample{– class Program {– public static float Average(params long[ ] v) {– long total, i;– for (i = 0, total = 0; i < v.Length; ++i)– total += v[i];– return (float)total / v.Length;– }– static void Main() {– float x = Average(1, 2, 3, 5);– Console.WriteLine("1、2、3、5的平均值为{0}", x);– x = Average(4, 5, 6, 7, 8);– Console.WriteLine("4、5、6、7、8的平均值为{0}", x);– Console.ReadLine();– }– }–}五、课堂练习•1. 编写一个控制台应用程序,完成下列功能。
– 1) 创建一个类,用无参数的构造函数输出该类的类名– 2) 增加一个重载的构造函数,带有一个string类型的参数,在此构造函数中将传递的字符串打印出来– 3) 在Main方法中创建属于这个类的一个对象,不传递参数– 4) 在Main方法中创建属于这个类的另一个对象,传递一个字符串“This is a string.”。
