1、1,1,2,7.1 理解分类,设计一个类的过程,就是对信息进行分类,将相关信息放到一个有意义的实体中的过程,3,7.2 封装的目的,封装是类的一个重要原则 中心思想:使用一个类的程序不应该关系类的内部实际如何工作 封装有时也称为信息隐藏 封装的目的: 将方法和数据合并到一个类中;换言之,为了支持分类 控制对方法和数据的访问;换言之,为了控制类的使用,类 和 对 象,类是对象的模板,它定义了对象的公共属性和行为(方法)。属性和方法共同称作类的成员对象是类的实例,是具体的对象必须使用 new 进行实例化才可以使用,类 和 对 象 3.1.1,类 对象人类 (模板) 阿土仔(具体化)姓名 姓名 =
2、阿土仔 性别 性别 = 男 使用卡片( ) 使用“陷害卡” 使用道具( ) 使用“飞弹”,属性,方法,类 和 对 象 3.1.1,类: 相对对象而言,类似于模板或蓝图 包含方法和数据,对象: 是类的实例 使用关键字 new 创建 对象具有状态和行为,对象,类,7,类概览,Class_definition:= class ,:= := := := := := := := := := := := := := := ,类成员被分成了三大类: 数据成员 函数成员 嵌套类型,8,数据成员包括:,成员变量: (也叫域)用于表示数据 成员变量可以用于类的特定实例(对象)-实例变量;也可以属于类自身(通过声明
3、为static),这时,它称为静态变量或类变量。 一个静态方法通过它的类(不是它的对象)来调用且它属于类而不属于对象。同样,一个静态变量属于类而不是任何特定的对象。 常量 成员变量也可以声明为只读(通过Readonly关键字) 常量成员的值在编译程序时被设置为源代码中指定的值,并且在所编译程序的整个生存期内均表示这一值。而只读成员变量在创建时给它赋值,并且在此对象生存期内保持此值。 事件按声明成员变量的相似方式来声明。用法大不一样。 事件驱动程序。程序执行的下一个动作取决于程序中触发的下一个事件。所以,让程序按语句书写顺序依次执行的思想恐怕得不到实现了。当运行一个GUI应用程序时就需要此异步功
4、能。,9,函数成员包括:,方法 构造函数 析构函数 属性 索引器 操作符,10,嵌套类型:,类体中定义的类、结构、枚举和接口,7.4 控制可访问性,一个方法或字段假如只允许从类的内部访问,就说他是私有的(private) 该关键字是默认的,但作为一种良好的编程习惯,应该显式地将方法和字段声明为private 一个方法或字段假如既能从类的内部访问,也能从外部访问,就说他是公共的(public) 注意: 方法中声明的变量默认是不初始化的 相反,类中的字段自动初始化为0,false或null 作为一个良好的编程习惯,应该显式地初始化字段,11,命名和可访问性,.NET Framework推荐以下字段
5、和方法的命名约定,它们是以可访问性为出发点来考虑的: public的标识符应该以大写字母开头,例如:Area 非public的标识符应该以小写字母开头,例如:radius 例外:类名是以大写字母开头,而构造器必须完全和类名相同,故一个private的构造器也应该以大写字母开头,12,13,7.4.1使用构造器,字段的构造器 创建一个对象时,对应的表示实例变量的内存被分配,这是对象创建过程的一部分。 但分配完成后(且在任何自动或人工初始化发生前),内存和实例变量仅表示上次使用后残留的垃圾。将这部分垃圾用于计算就会导致错误的结果。例: distance = time * speed; 没有实例化的
6、实例变量很容易导致错误的发生,14,1、自动初始化 short、int、float、decimal等为0 char类型值赋值为Unicode字符、u0000 bool类型值初始为false 引用类型初始化为null 一般情况下,不推荐让运行时自动执行实例变量的初始化: 阅读源代码的程序员不能看到此初始值。他或她可能不知道前面所列的缺省初始值, 缺省值随编译器的不同、同一软件公司发行编译器版本的不同而有所差异。,15,2、在声明中给实例变量赋值 class Account private decimal balance = 100;. private decimal balance = ;,16
7、,如果我们需要一个if语句来确定恰当的初始化值,例如: 一个银行可能通过“如果在2004年6月1日前新开帐户,免费赠送开户额200元;如果在6月1日和7月1日期间新开帐户,则赠送开户额100元,7月1日后免费赠额为0,敬请把握良机”来近行特别宣传。,17,以上代码看似正确,但实际是无效的。C#只允许if语句及类似语句位于函数成员体的内部。class Account private decimal balance;public void Initialize()if(OpeningDate is earlier than 1st of June)balance = 200;else if (Op
8、eningDate is in between 1st of June and 1st of July)balance = 100;elsebalance = 0; ,18,Account myAccount = new Account(); myAccount.Initialize();,19,使用实例构造函数,类的实例构造函数是一个有专门作用的方法,当用new关键字创建此类的一个对象时调用它。 实例构造函数用于执行实例变量的初始化及其他创建对象所需的动作。 定义实例构造函数,20,Instance_Constructor_Definition:= Constructor_Identifie
9、r ( ) 其中:=private:=public:= protected:= internal:= : base ( ):= : this ( ) :=:=;,21, ,必须等同于构造函数所在的类标识符。例如一个叫Dog的类中的构造函数必须也叫Dog。实例构造函数不能返回值。所以,没有指定返回类型,甚至也没有使用void关键字。导致执行此实例构造函数前的另一个实例构造函数。,22,说明: 与常规方法一样,实例构造函数定义的构造函数标示符后必须用一个圆括号来包含可选的形式参数列表。当用new关键字创建一个新对象时,参数值被传递给这些形式参数。 如果类包含两个或更多的实例构造函数,在声明实例构造
10、函数语句之前,你可以选择性地执行同一个类中的实例构造函数。通过将构造函数初始化器this放在形式参数列表后面来实现。,23,调用实例构造函数 Cat myCat = new Cat(); 圆括号看似没有必要,那为什么非要包含它呢?我们已经知道,new cat()创建一个Cat类的新对象并返问一个对此对象的引用在这里,就是将它赋值给myCat。 任何方法调用必在方法名后面加一对圆扩号 按同样方法调用一个实例构造函数。,24,类名(在这里就是Cat)后的圆括号可以像常规方法样包括一个参数列表(): 此参数列表必须与实例构造函数中对应的形式参数列表匹配。如果存在两个或更多个实例构造函数(通常为重载)
11、,形式参数列表与传递的参数列表匹配的实例构造函数被执行。,25,一个带空形式参数列表的实例构造函数称作缺省实例构造函数。缺省实例构造函数的另一个术语是无参数实例构造函数。,class Tree private int age = 0;. ,class Tree private int age = 0;public Tree(); . ,26,注意: 如果你编写一个或多个非缺省实例构造函数,需要的话,你必须编写自己的缺省实例构造函数: 如果你在类中包含一个或多个实例构造函数,C#编译器不会包含一个缺省实例构造函数。例如:,class Car private int odometer;public
12、 Car (int initialOdometer)odometer = initialOdometer; ,Car yourCar = new Car(); /Invalid. Default,27,技巧: 如果你在类中包含一个或多个实例构造函数,最好同时包含一个缺省实例构造函数 即使你认为类在编写时并不需要一个缺省构造函数,但此类在频繁使用过程中,目的和场合稍有不同时,可能突然需要调用缺省构造函数,当你在构建类时添加缺省实例构造函数就省事了。,28,重载实例构造函数,为了区分重载实例构造函数中包含的不同形式参数列表,像对待常规重载方法一样,编译器遵循相同的判断过程。,29,构造函数初始化器
13、,假设无论调用哪一个重载实例构造函数,某个类都需要执行一套核心初始化语句。 我们可以首先在每个实例构造函数中包含相同的初始化语句集,然后我们在顶端添加此实例构造函数特定的语句。这样虽然能正常工作,但每个实例构造函数都将承担相同的核心语句执行任务,而且你也要重复复制和粘贴这些语句。 另一种办法是,仅在缺省实例构造函数中放入核心语句,并让每个重载实例构造函数通过构造函数初始化器来调用它,这样就可以删除第一种途径中多余的代码了。,30,01: class Account 02: 03: private decimal balance; 04: private decimal interestRate
14、; 05: private string holderName; 06: 07: public Account(decimal initialBalance, decimal initialInterestRate, 08: string initialHolderName) 09: 10: balance = initialBalance; 11: interestRate = initialInterestRate; 12: holderName = initialHolderName; 13: BankStatistician.AccountAdded(balance); 14: Con
15、sole.WriteLine(“New account opened“); 15: 16: 17: public Account(string initialHolderName) : this (0,0,initialHolderName) 18: 19: Console.WriteLine(“Note: Only initial holder name supplied“); 20: 21: 22: public Account() : this (0, 0, “Not assigned yet“) 23: 24: Console.WriteLine(“Note: No initial i
16、nformation supplied“); 25: ,31,私有实例构造函数,private代替public 一个私有实例构造函数不能由此类的外部任何地方调用,这与私有方法和私有实例变量相似。 一个私有实例构造函数不能用于创建对象,所以只包含私有实例构造函数的类可有效防止从此类的外部对类中的任何对象进行实例化。 这一原理通常用于仅包含静态成员的类中。这些静态类根本没有必要实例化,这样可阻止在程序的其他部分对他们的偶然实例化。,32,静态构造函数,class Fish private static int numberOfFishCreated;static Fish()-只能由运行时访问nu
17、mberOfFishCreated = 0;. ,Constructor_identifier必须与静态构造函数所在的类同名。 静态构造函数没有指定任何参数 静态构造函数没有指定任何访问修饰符,分部类,一个类可能包含大量方法、字段、构造器以及其他内容 一个功能齐全的类可能相当大 C#中,可以将一个类的源代码拆分到单独的文件中 这样一来,大型类就可以使用较小的、更容易管理的部分来进行组织 使用partial关键字来定义类的不同部分,33,partial class Circle public Circle() / default constructor this.radius = 0; publ
18、ic Circle(int initialRadius) / overloaded constructor this.radius = initialRadius; The contents of circ2.cs look like this: partial class Circle private int radius; public double Area() return Math.PI * this.radius * this.radius; ,34,数据成员(data member),实例变量表示与单个对象相关的数据,当创建一个对象时,它的实例变量赋于对应于此对象的值,而且这些值
19、在对象的生存期间与对象保持一致。 如果我们要表示属于同一类的整组对象相关的信息片断? BankSimulation的例子:我们必须先决定账户数量,并且此数量在程序中固定不变。假定我们放宽此条件,在程序执行的任何时候都能打开或关闭此账户,而且还希望跟踪创建Account对象的总数量。 实现: class Account public uint totalAccountCreated; ,35,此方法的弊端:,数据复制:totalAccountCreated导致每个Account对象表示一个等同于其他所有Account对象的值。这不仅浪费了计算机内存,也混淆了程序员的注意力。 由于所有Accoun
20、t对象的代码更改而导致的代码效率低下:每次创建一个新Account对象,Bank对象必须修改(将变量值加1)所有的Account对象。每次添加一个新的Account,为了进行更新,迭代语句将不得不遍历整个Account对象集合。,36,37,解决的方法:,只在一个地方保存totalAccountsCreated,将它从每个对象中删除。 因为Account类定义了所有Account对象的属性,所以将它放在Account类中。 通过static关键字实现,class Account .public static uint totalAccountsCreated; /Efficient. ,类似于
21、全局变量 只存在一个totalAccountsCreated的复制,它属于整个Account类而不属于单个对象。 totalAccountsCreated由所有Account对象共享。 totalAccountsCreated有一个值,即使没有创建Account对象也是如此。 totalAccountsCreated可以通过Account类访问。,38,39,静态成员变量,实例变量表示与单个对象相关的数据,当创建一个对象时,它的实例变量赋于对应于此对象的值,而且这些值在对象的生存期间与对象保持一致。 如果我们要表示属于同一类的整组对象相关的信息片断? 注意: 静态成员变量也叫做共享字段,类似于
22、全局变量 一个静态成员变量使用类名加点操作符 不能通过对单个对象使用点操作符来访问。,40,01: using System; 02: using System.Collections; 03: 04: class Account 05: 06: public static uint totalAccountsCreated = 0; 07: 08: public Account() 09: 10: totalAccountsCreated+; 11: 12: 13: 14: class Bank 15: 16: private ArrayList accounts; 17: 18: publ
23、ic Bank() 19: 20: Console.WriteLine(“Congratulations! You have created a new bank“); 21: accounts = new ArrayList(); 22: 23:,41,24: public void AddNewAccount() 25: 26: accounts.Add(new Account(); 27: Console.WriteLine(“New account added!“); 28: PrintStatistics(); 29: 30: 31: public void RemoveFirstA
24、ccount() 32: 33: if (accounts.Count 0) 34: 35: accounts.RemoveAt(0); 36: Console.WriteLine(“Account removed!n“); 37: PrintStatistics(); 38: 39: else 40: 41: Console.WriteLine(“Sorry no more current accounts“); 42: 43: 44: 45: public void PrintStatistics() 46: 47: Console.WriteLine(“Number of current
25、 accounts:“ + accounts.Count + 48: “nNumber of new accounts created: “ + Account.totalAccountsCreated); 49: 50: ,42,52: class DynamicBankSimulation 53: 54: private static Bank bigBucksBank; 56: public static void Main() 57: 58: string command; 60: bigBucksBank = new Bank(); 61: do 62: 63: PrintMenu(
26、); 64: command = Console.ReadLine().ToUpper(); 65: switch (command) 66: 67: case “A“: 68: bigBucksBank.AddNewAccount(); break; 70: case “E“: 71: Console.WriteLine(“Bye Bye!“);break; 73: case “R“: 74: bigBucksBank.RemoveFirstAccount();break; 76: case “P“: 77: bigBucksBank.PrintStatistics(); break; 79
27、: default: 80: Console.WriteLine(“Invalid choice“); 81: break; 82: 83: while (command != “E“); 84: ,43,86: private static void PrintMenu() 87: 88: Console.WriteLine(“nWhat would you like to do?n“ + 89: “A) Add new accountn“ + 90: “R) Remove accountn“ + 91: “P) Print statisticsn“ + 92: “E) End sessio
28、nn“); 93: 94: ,44,语法箱,Data_member_declaration:= static const | readonly= , = .; where:= public:= private:= protected:= internal:= protected internal,45,函数成员,实例方法:实例函数组 静态方法:静态方法组 类的所有对象共享每个实例方法的一个拷贝:编译器将此方法所调用对象的引用作为一个参数暗中传递给实例方法。,46,Method:=where := () := := := new:= static:= virtual:= sealed:= ove
29、rride:= abstract:= extern:= public:= private:= protected:= internal:= protected internal:= void:= ,47,调用一个方法的方式是:在方法的名字后面加上一对包含参数的圆括号,并与形式参数匹配。,48,静态方法,调用main方法,运行时不会创建任何对象。 与其它方法一样,静态方法的定义必须放在类体中。 class MyClass() .public static void Main() . 运行时可以直接通过类名调用:Myclass.Main(),49,调用一个静态方法的三种途径:,从方法所属类的内部调
30、用,此时不需要任何类名或对象名作为前缀,50,从所属类外部调用 如果定义方法所在类存在任何一个对象,则可使用此对象名加点操作符来调用: Rocket rocketA = new Rocket(); rocketA.Average(.). 不管定义方法所在类是否存在任何对象,都可以通过类名加加点操作符来调用 注意:最好使用类名而不是对象名,因为类名调用格式清楚的表示我们正调用一个静态方法。,51,何时使用静态方法,不能明确指定属于某一特定对象的方法应该为静态 class MyMath .public double Average (params double numbers). 如果要调用Ave
31、rage方法: MyMath mathObject=new MyMath(); mathObject.Average() 将Average声明为static public static double Average (params double numbers) 调用:MyMath.Average(),52,技巧:如果程序包含若干不与任何特定对象相关联的方法,提供的功能属于同一个范畴,那么为这些方法创建一个类并声明为static。 静态方法遍布.Net框架中。例:System.Math类包含24个静态数学方法。Console类包含Read、ReadLine、Write、WriteLine等,5
32、3,2、在某些情况下,使用一个静态方法来访问静态成员变量,54,使用const关键字创建静态字段,const字段是静态字段,但在声明时不适用static关键字 const字段一旦声明,其值永远不会改变 class Math cublic const double PI=3.1415926; ,55,所有的静态变量必须声明为Private,当从类的外部访问时,私有静态变量只能通过静态方法进行访问或通过在同一类中作为变量定义的实例方法来访问。 不能从同一个类的静态方法内直接访问和调用实例变量及实例方法 静态方法与一个类而不是与任何特定对象相关联。 实例方法和实例变量属于一个类的特定对象。 静态和实
33、例类成员的这一区别导致不能从同一类的静态方法内部直接调用一个实例方法或直接访问一个实例变量。,56,01: using System; 02: 03: class Light 04: 06: public const double Speed = 299792; 07: private double timeTraveled; 08: private double distanceTraveled; 09: 10: public static double CalculateDistance (double seconds) 11: 12: return seconds * Speed; 13
34、: 14: 15: public static double CalculateTime (double distance) 16: 17: Light someLight = new Light(); 18: someLight.SetDistanceTraveled(distance); 19: return someLight.GetTimeTraveled(); 20: 21: 22: public void SetTimeTraveled (double newTimeTraveled) 23: 24: timeTraveled = newTimeTraveled; 25: ,57,
35、27: public void SetDistanceTraveled (double newDistanceTraveled) 28: distanceTraveled = newDistanceTraveled; 32: public double GetDistanceTraveled () 33: return CalculateDistance (timeTraveled); 37: public double GetTimeTraveled () 38: return distanceTraveled / Speed; 41: 43: class SolarSystem 44: 4
36、5: public static void Main() 46: 47: Light sunRay = new Light(); 49: Console.WriteLine(“Speed of light: 0 kilometers per hour“, Light.Speed); 50: sunRay.SetTimeTraveled(240); 51: Console.WriteLine(“The sunray has traveled 0 kilometers after 240 seconds“, 52: sunRay.GetDistanceTraveled(); 53: Console
37、.WriteLine(“Light travels 0 kilometers after 480 seconds“, 54: Light.CalculateDistance(480); 55: Console.WriteLine(“It takes 0:N2 seconds for a ray of sunshine“, 56: Light.CalculateTime(150000000); 57: Console.WriteLine(“to travel from the sun to the earth“); 58: 59: ,58,用this关键字自引用,this是一个特殊的引用(指针)
38、,它指向的是“自己”,也就是当前对象 当需要对当前对象的引用时,this关键字写在方法体内。 this的作用: 点取成员 区分同名变量 将当前对象(自己)作为参数,传递给其他对象的方法 作为方法名表示构造方法,59,60,01: using System; 03: class Book 04: 05: private string title; 06: private uint numberOfPages; 07: private double weight; 09: public void SetAll(string title, uint numberOfPages, double wei
39、ght) 10: 11: this.title = title; 12: this.numberOfPages = numberOfPages; 13: this.weight = weight; 14: 16: public double GetWeight() 17: 18: return weight; 19: 21: public void PrintShippingCost() 22: 23: Console.WriteLine(“nCost of shipping“ 0“: 1:C “,title, Dispatcher.ShippingCost(this); 25: 27: pu
40、blic Book GetHeavierBook (Book aBook) 28: 29: if (weight aBook.GetWeight() 30: return this; 31: else 32: return aBook; 33: 34: ,区别形式参数、本地变量与实例变量,this的作用1,61,36: class Dispatcher 37: 38: public static decimal ShippingCost (Book bookToSend) 39: 40: return 5m + (decimal)(bookToSend.GetWeight() * 3); 41
41、: 42: 43: 44: class VirtualBookshop 45: 46: public static void Main() 47: 48: Book myBook = new Book(); 49: Book yourBook = new Book(); 50: Book heavierBook; 51: 52: myBook.SetAll(“计算机系统结构“, 400, 2.3); 53: Console.WriteLine(“Shipping cost: 0:C “, Dispatcher.ShippingCost (myBook); 54: myBook.PrintShi
42、ppingCost(); 56: yourBook.SetAll(“微机原理“, 610, 3.1); 57: heavierBook = yourBook.GetHeavierBook(myBook); 58: heavierBook.PrintShippingCost(); 59: 60: ,作为一个参数将当前对象的引用传递给另一个方法,将当前对象的引用返回给方法的调用者,62,只读成员(readonly member),与常量成员(由const关键字声明)一样,只读成员也用于表示一个不变化的值。但是,赋值给常量的值必须在源代码中指定,如: const decimal MassOfElec
43、tron = 9.0E-28m; 只读成员的值直到程序运行时才知道 只读成员的值在一个构造函数中初始化,而且此后不能再修改。所以,常量成员在编译后程序的整个生存期内保持不变,而只读成员只在此对象的生存期内保持不变。 常量成员的类型限于以下之一:byte、sbyte、short、ushort、int、uint、long、float、double、decimal、char、bool、enum、string 只读成员可以为任何类型,63,01: using System; 02: 03: class Account 04: 05: public readonly string AccountNumb
44、er; 06: 07: public Account (string permanentAccountNumber) 08: 09: AccountNumber = permanentAccountNumber; 10: 11: 12: 13: class SimpleBank 14: 15: public static void Main() 16: 17: Account yourAccount = new Account(“8487-9873-9938“); 18: Console.WriteLine(“Your account number: “ 19: + yourAccount.A
45、ccountNumber); 20: 21: ,64,13.4 垃圾回收:自动动态内存管理,01: using System; 02: 03: class Account 04: 05: 06: 07: 08: class Bank 09: 10: private Account bankAccount; 11: 12: public void SetAccount (Account newAccount) 13: 14: bankAccount = newAccount; 15: 16: 17:,18: class Tester 19: 20: public static void Main
46、() 21: 22: Bank bank1; 24: Account account1; 25: Account account2; 27: account1 = new Account(); 29: account1 = null; 31: account1 = new Account(); 32: account2 = new Account(); 34: account1 = account2; 36: bank1 = new Bank(); 37: bank1.SetAccount(new Account(); 39: bank1 = null; 41: DoingAccountStuff(); 42: 44: public static void DoingAccountStuff() 45: 46: Account localAccount; 47: localAccount = new Account(); 48: 49: ,