• 1. 第八章C#高级编程
  • 2. 目标理解继承 在C# 中使用继承 在C#中使用接口 在C#中使用方法的重写 理解属性及其不同的类型、实现 理解和使用索引器 实现委托 定义和触发事件2
  • 3. 继承 2-1Class Base { // 成员变量 int basevar; // 成员函数 Base_fun1() { // 定义 } ……. …….Class Derived : Base { // 成员变量 int derivedvars; // 成员函数 Derived_fun1() { // 定义 } ……. …….基类 void main() { Derived dr_obj = new Derived() ; dr_obj.Base_fun1(); }无需重新编写代码派生类3
  • 4. 狗马继承 2-2动物基类派生类继承的层次结构示例Class Animal { // 成员变量 int eyes, nose; Animal() { eyes = 2; nose = 1; } Pet_Animal() { // 定义 } }基类Class Dog : Animal { // 成员变量 // 成员函数 private Barking() { // 定义 } private Wagging_Tail() { } } 派生类4
  • 5. 继承 C# 中的类 public class Graduate: Student, Employee { // 成员变量 // 成员函数 } 多重继承 允许多重接口实现X5
  • 6. 演示public class Student:Person { private string _school; private uint _eng; private uint _math; private uint _sci; public void GetMarks() { Console.WriteLine(“请输入学校名称"); _school = Console.ReadLine(); Console.WriteLine("请分别输入英语、数学和自然科学的分数。"); _eng = uint.Parse(Console.ReadLine()); _math = uint.Parse(Console.ReadLine()); _sci = uint.Parse(Console.ReadLine()); Console.WriteLine(“所得总分为:{0}",_eng+_math+_sci); } }派生类public class Person { private string _name; private uint _age; public void GetInfo() { Console.WriteLine("请输入您的姓名和年龄"); _name = Console.ReadLine(); _age = uint.Parse(Console.ReadLine()); } public void DispInfo() { Console.WriteLine("尊敬的 {0},您的年龄为 {1} ", _name, _age); } }基类static void Main(string[] args) { Student objStudent = new Student(); objStudent.GetInfo(); objStudent.DispInfo(); objStudent.GetMarks(); }调用的基类成员无法实现 GetInfo() 和 DispInfo() 方法6
  • 7. 演示public class Person { private string _name; private uint _age; public void GetInfo() { Console.WriteLine("请输入您的姓名和年龄"); _name = Console.ReadLine(); _age = uint.Parse(Console.ReadLine()); } public void DislayInfo() { Console.WriteLine("尊敬的 {0},您的年龄为 {1}", _name, _age); } }public class Student:Person { private string _schoolname; private uint _engscore; private uint _mathscore; private uint _sciscore; private uint _totscore; public uint GetMarks() { Console.WriteLine(“请输入学校名称"); _schoolname = Console.ReadLine(); Console.WriteLine("请输入英语、数学和自然科学的分数。 "); _engscore = uint.Parse(Console.ReadLine()); _mathscore = uint.Parse(Console.ReadLine()); _sciscore = uint.Parse(Console.ReadLine()); _totscore = _engscore + _mathscore + _sciscore; Console.WriteLine("所得总分为:{0} ",_totscore); return _totscore; }}基类派生类public class UnderGraduate:Student { public void Check() { Console.WriteLine("要上升一级,要求总分不低于 150 "); if(this.GetMarks() >=150) Console.WriteLine(“pass"); else Console.WriteLine(“not pass"); } }派生类public static void Main(string[] args) { UnderGraduate objUnderGraduate = new UnderGraduate(); objUnderGraduate.GetInfo(); objUnderGraduate.DisplayInfo(); objUnderGraduate.Check(); }7
  • 8. 用于从派生类中访问基类成员 可以使用 base 关键字调用基类的构造函数 关键字 base8
  • 9. 调用 base 构造函数 public class Student:Person { private uint id; //调用 Person 构造函数 public Student(string name,uint age,uint id):base(name,age) { this.id = id; Console.WriteLine(id); } } :base 关键字将调用 Person 类构造函数9
  • 10. 演示public class Person { public string _name; public uint _age; public Person(string name, uint age) { this._name = name; this._age = age; Console.WriteLine(_name); Console.WriteLine(_age); } }public class Student:Person { private uint _id; public Student(string name, uint age, uint id):base(name, age) { this._id = id; Console.WriteLine(_id); } }还将调用 Base 构造函数static void Main(string[] args) { //构造 Student Student objStudent = new Student("XYZ", 45, 001); }10
  • 11. 关键字 overrideClass Base {  // 成员变量 int basevar;  // 成员函数 GetInfo() {  // 定义 } ……. …….Class Derived : Base { // 成员变量 int derivedvars; // 成员函数 override GetInfo() { // 定义 } ……. …….基类派生类base 方法的新实现11
  • 12. 关键字 virtual [访问修饰符] virtual [返回类型] 方法名 ( [参数列表] ) { ... // Virtual 方法实现 ... }public virtual void Func() { Console.WriteLine(“这是 virtual 方法,可以被重写"); }12
  • 13. //将执行派生类的变量//要求 new 访问修饰符将输出派生类中的 val相同字段new隐藏继承成员关键字 new13
  • 14. class Student { public virtual void StuInfo() { Console.WriteLine(“此方法显示学生信息"); }}class ExpStudent: Student { public override void StuInfo() { base.StuInfo(); Console.WriteLine(“此方法重写 base 方法"); }}static void Main(string[] args) { ExpStudent objStudent = new ExpStudent(); objStudent.StuInfo(); Student objSuper = objStudent; objSuper.StuInfo(); }14
  • 15. 抽象类和抽象方法 2-1 abstract class ClassOne { //类实现 }访问修饰符派生类的基类不能实例化15
  • 16. abstract class Base { // 成员变量 int basevar; // 成员函数 abstract void base_fun1(parameters); // 无法实现 ……. }抽象方法class Derived : Base { // 成员变量 int derivedvars; // 成员函数 override void Base_fun1(parameters) { // 实际实现 ... }抽象类派生类提供重写方法原型必须重写 抽象类和抽象方法 2-2 16
  • 17. 演示using System; namespace Example_1 { abstract class ABC { public abstract void AFunc(); public void BFunc() { Console.WriteLine(“这是一个非抽象方法!"); } } class Derv : ABC { public override void AFunc() { Console.WriteLine(“这是一个抽象方法! "); } }抽象类 – 不能实例化派生类 – 重写方法static void Main(string[] args) { Derv objB = new Derv(); objB.AFunc(); objB.BFunc(); }17
  • 18. abstract class MyAbs { public abstract void AbMethod(); } //派生类 class MyClass : MyAbs { public override void AbMethod() { Console.WriteLine(“在 MyClass 中实现的抽象方法"); } } //派生自 MyClass 的子类 class SubMyClass:MyClass { public void General() { //未实现 AbMethod 抽象方法 Console.WriteLine("在 SubMyClass 中未实现的抽象方法 "); } }static void Main(string[] args) { SubMyClass objSubClass = new SubMyClass(); objSubClass.General(); }18
  • 19. 接口 3-1OFFON请按开关按钮:ON/OFF两种方法 ON OFF19
  • 20. 接口 3-2class IBase { void method1(); int method2(); //没有实现 ……. }接口interface只有方法声明没有实现20
  • 21. public interface IPicture { int DelImage(); void ShowImage(); }隐式声明为 public无访问修饰符示例中的 IPicture接口用于演示接口接口 3-321
  • 22. 演示public class MyPicture : IPicture { //第一个方法的实现 public int DelImage() { Console.WriteLine(“DelImage 实现!"); return(1); } //第二个方法的实现 public void ShowImage() { Console.WriteLine(“ShowImage 实现!"); } }static void Main(string[] args) { MyPicture objM = new MyPicture(); objM.ShowImage(); int t = objM.DelImage(); Console.WriteLine(t); }派生自 IPicture 接口22
  • 23. 演示public interface IPicture { int DelImage(); void ShowImage(); } public class MyPicture : BaseIO, IPicture { public int DelImage() { Console.WriteLine(“DelImage 实现!"); return(1); } public void ShowImage() { Console.WriteLine(“ShowImage 实现!"); } }public class BaseIO { public void Open() { Console.WriteLine("BaseIO 的 Open 方法"); } }static void Main(string[] args) { MyPicture objM = new MyPicture(); objM.ShowImage(); int val = objM.DelImage(); Console.WriteLine(val); objM.Open(); }23
  • 24. 多重接口实现C# 不允许多重类继承 但C#允许多重接口实现 这意味着一个类可以实现多个接口 24
  • 25. 演示public interface IPictManip { void ApplyAlpha(); } //第二个接口 public interface IPicture { int DelImage(); void ShowImage(); } public class BaseIO { public void Open() { Console.WriteLine(“BaseIO 的 Open 方法"); } }static void Main(string[] args) { MyPicture objM = new MyPiture(); objM.ShowImage(); objM.DelImage(); objM.ApplyAlpha(); objM.Open(); }25
  • 26. 显式接口实现在 C# 中,只要不发生命名冲突,就完全可以允许多重接口实现public interface IPictManip { void ApplyAlpha(); }public interface IPicture { void ApplyAlpha(); }public class MyPicture : BaseIO, IPicture, IPictManip { public int ApplyAlpha() { ....... ....... } }?使用显式接口实现26
  • 27. 演示public class MyPicture : BaseIO, IPicture, IPictManip { public int DelImage() { Console.WriteLine(“DelImage 实现!"); return(1); } public void ApplyAlpha() { Console.WriteLine(“ApplyAlpha 实现!"); } void IPicture.ShowImage() { Console.WriteLine(“ShowImage 的 IPicture 实现"); } void IPictManip.ShowImage() { Console.WriteLine(“ShowImage 的 IPictManip 实现"); } }显式接口实现static void Main(string[] args) { MyPicture objM = new MyPicture(); IPicture Pict = objM; //IPicture 引用 Pict.ShowImage(); IPictManip PictManip = objM; //IPictManip 引用 PictManip.ShowImage(); }27
  • 28. 演示public interface IPicture { int DelImage(); } public interface IPictManip { void ApplyAlpha(); void ShowImage(); } //继承多重接口 public interface IPictAll:IPicture, IPictManip { void ApplyBeta(); }public class MyPicture:IPictAll { public int DelImage() { Console.WriteLine(“DelImage 实现!"); return(1); } public void ApplyAlpha() { Console.WriteLine(“ApplyAlpha 实现!"); } public void ApplyBeta() { Console.WriteLine(“ApplyBeta 实现!"); } public void ShowImage() { Console.WriteLine(“DisplayImage 实现!"); } }static void Main(string[] args) { MyPicture objM = new MyPicture(); objM.ShowImage(); int val = objM.DelImage(); Console.WriteLine(val); objM.ApplyAlpha(); objM.ApplyBeta(); }28
  • 29. 自定义接口如老师和学员都可以收作业, 那么收作业的方法应该放在哪个类?A:Teacher类B:Student类C:两个都放D:Person类E:重新定义造成代码冗余如果增加一个工程师类,他不会收作业如果继承这个类,Person类怎么办?调用收作业的方法不需要改变自定义一个接口来实现IHomeworkCollector29
  • 30. 自定义接口public interface IHomeworkCollector { void CollectHomework(); }public class Student : Person, IHomeworkCollectorpublic void CollectHomework() { MessageBox.Show("报告老师!作业收集完毕!"); }1、定义一个收作业的接口2、在有收作业功能的类实现该接口3、不同的类收作业方式不同30
  • 31. 使用自定义接口1、接口作为参数使用private void DoCollectHomework(IHomeworkCollector collector) { collector.CollectHomework(); }无论谁收作业这里都不需要做任何改变2、接口作为返回值使用DoCollectHomework(scofield);private IHomeworkCollector CreateHomeworkCollector(string type){ switch (type) { case "student": collector = new Student("Scofield", Genders.Male, 28, "越狱狱"); break; … } return collector }返回一个实现该接口的对象collector.CollectHomework();31
  • 32. 作为返回值和参数的意义接口作为参数 传递了实现接口的对象 接口作为返回值 返回一个实现了接口的对象32
  • 33. 接口和抽象类的对比抽象类接口不 同 点用 abstract 定义用 interface 定义只能继承一个类可以实现多个接口非抽象派生类必须实现抽象方法实现接口的类必须实现所有成员需要override实现抽象方法直接实现相同点不能实例化包含未实现的方法派生类必须实现未实现的方法33
  • 34. 委托Multiply(int,int) { …. } Divide(int,int) { …. } 在运行时确定调用哪种方法委托和方法必须具有相同的签名--- public delegate Call(int n1, int n2); ---34
  • 35. 定义委托 2-1 [访问修饰符] delegate 返回类型 委托名(); 语法class Delegates { public delegate int Call(int n1, int n2); class Math { public int Multiply(int n1, int n2) { // 实现 } public int Divide(int n1, int n2) {// 实现 } }将方法与委托关联起来class TestDelegates { static void Main() { Call objCall; Math objMath = new Math(); objCall = new Call(objMath.Multiply); } }35
  • 36. 定义委托 2-2class Delegates { // 委托定义 public delegate int Call(int n1, int n2); class Math { // 乘法方法 public int Multiply(int n1, int n2) { return n1*n2; } // 除法方法 public int Divide(int n1, int n2) { if(n2!=0) return n1/n2; } }static void Main(string[] args) { // 委托的对象 Call objCall; // Math 类的对象 Math objMath = new Math(); // 将方法与委托关联起来 objCall = new Call(objMath.Multiply); // 将委托实例化 result = objCall(4, 3); System.Console.WriteLine("结果为 {0}", result); } 将方法与委托关联起来36
  • 37. 事件抢答者 宣布人抢答者“请听题~”集中注意力聆听其他人事件源事件的发布者事件的订阅人不关心未订阅该事件定义事件 为对象订阅该事件 将发生的事件通知给订阅人37
  • 38. 定义事件[访问修饰符] event 委托名 事件名; 语法public delegate void delegateMe(); private event delegateMe eventMe;38
  • 39. 订阅事件 eventMe += new delegateMe(objA.Method); eventMe += new delegateMe(objB.Method);39
  • 40. 通知订阅对象 if(condition) { eventMe(); }调用订阅特定事件的对象的所有委托40
  • 41. 示例class TestEvents { [STAThread] static void Main(string[] args) { // 委托的对象 Delegate objDelegate = new Delegate(); // ClassA 的对象 ClassA objClassA = new ClassA(); // ClassB 的对象 ClassB objClassB = new ClassB(); // 订阅该事件 objDelegate.NotifyEveryOne += new Delegate.MeDelegate(objClassA.DispMethod); objDelegate.NotifyEveryOne += new Delegate.MeDelegate(objClassB.DispMethod); objDelegate.Notify(); } }class Delegate { // 定义委托 public delegate void MeDelegate(); // 定义事件 public event MeDelegate NotifyEveryOne; public void Notify() { // 如果事件不为 null if(NotifyEveryOne != null) { Console.WriteLine("触发事件:"); // 触发事件 NotifyEveryOne(); } } }class ClassA { public void DispMethod() { Console.WriteLine(“Class A 已接到 NotifyEveryOne 事件的通知!"); } } // 第二个类 class ClassB { public void DispMethod() { Console.WriteLine(“Class B 已接到 NotifyEveryOne 事件的通知! "); } } 41
  • 42. 什么是程序集为什么只运行这个.exe文件就能使用我们程序的所有功能?42
  • 43. 什么是程序集 .NET框架应用程序的生成块 包含编译好的代码的逻辑单元 创建的每个项目文件(project)都会产生一个程序集dll MyNewsReader.exe是“新闻快客”的程序集 程序集的结构 程序集清单 类型元数据 IL代码 资源向其他应用程序公开,并由这些应用程序使用元数据是二进制信息程序中的图片、音乐文件43
  • 44. 程序集清单程序集清单:元数据重要部分该信息用于从程序集导出的类型有关被引用程序集的信息 控制对该程序集的类型和资源的引用如 何映射到包含其声明和实现的文件类型引用信息 构成该程序集的文件程序集中所有文件的列表 如果已经为程序集提供了一个强名称,则为来自发行者的公钥强名称信息 有关该程序集支持的区域性或语言的信息区域性 主版本号和次版本号,以及修订号和内部版本号版本号 指定程序集名称的文本字符串程序集名称 说明信息 44
  • 45. ILDasmILDasm 反编译的工具。 可以查看IL汇编代码 可以看到类和方法演示: 使用ILDasm 45
  • 46. Visual Studio创建程序集Visual Studio的所有应用程序都创建程序集AssemblyInfo.cs用于配置程序集的属性46
  • 47. AssemblyInfo.cs介绍 AssemblyInfo.cs文件[assembly: AssemblyTitle("My News Reader")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jade Bird")] [assembly: AssemblyProduct("My News Reader")] [assembly: AssemblyCopyright("版权所有 (C) Jade Bird 2007")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")]47
  • 48. 程序集与 internal 访问修饰符internal修饰符用于程序集级别指定访问性 internal修饰的成员不能被其它程序集访问访问权限类内部同一程序集的派生类同一程序集的其他类不同程序集的派生类不同程序集的其他类priavte可以不可以不可以不可以不可以protected 可以可以不可以可以不可以internal可以可以可以不可以不可以public可以可以可以可以可以48
  • 49. 什么是序列化Profile对象界面语言:英语 RssFeed对象 存储 介质存储序列化是将对象的状态存储到特定存储介质中的过程… 代理服务器 49
  • 50. 特性[Serializable] abstract class FeedBase标识这个类是可序列化的可序列化就是这个类的一个特性描述性关键字 对程序中的元素如:类、字段、方法、属性 命名时以Attribute结尾: SerializableAttribute 使用时省略Attributepublic sealed class SerializableAttribute 特性其实是一个类class Program { [Obsolete("不要使用旧的方法, 请使用新的方法", true)] static void Old() { } static void New() { } public static void Main() { Old(); } }演示 MyAttribute 50
  • 51. 使用序列化fileStream = new FileStream("profile.bin", FileMode.Create); BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(fileStream, Profile);[Serializable] abstract class FeedBase要存储的对象标记为可序列化,包括他的父类和属性的类使用二进制方式存储对象二进制格式化器将对象以二进制方式格式化为流Serialize ( Stream serializationStream, Object graph) 流对象Serialize的用法:51
  • 52. 序列化的过程格式化程序 对象可否序列化子类成员 可否序列化将对象格式化写入存储介质异常退出序列化对象中的子类成员 YYNN52
  • 53. 反序列化把Profile对象存储成文件,怎么取出来呢?读取反序列化则是从特定存储介质中的数据重新构建对象的过程 存储 介质Profile对象界面语言:英语 RssFeed对象 … 代理服务器 数据转换为对象53
  • 54. 使用反序列化将序列化好的Profile数据反序列化为对象fileStream = new FileStream("profile.bin", FileMode.Open); BinaryFormatter bf = new BinaryFormatter(); Profile = (Profile)bf.Deserialize(fileStream); 将指定流反序列化类型转换public Object Deserialize ( Stream serializationStream ) Deserialize的用法:流对象需要类型转换54
  • 55. 序列化与反序列化几个重要的类 SerializableAttribute Formatter 可以将对象从一个应用程序发送到另一个应用程序中 使用场合 Web Service 分布式应用55
  • 56. 反射可以ILDasm反编译工具浏览一个dll和exe的构成这种机制叫做反射(Reflection)应用程序或dll类的属性类的方法应用程序信息…用于在运行时通过编程方式获得类型信息反射56
  • 57. 现场演示通过代码演示获取dll的版本号using System.Reflection;class Program { static void Main(string[] args) { string version = Assembly.LoadFile(@"D:\MyNewsReader.exe") .GetName().Version.ToString(); Console.WriteLine(version); } }引入命名空间 反射57
  • 58. 总结2-1 继承是获得现有类的功能的过程 创建新类所根据的基础类称为基类或父类,新建的类则称为派生类或子类 base 关键字用于从派生类中访问基类成员 override 关键字用于修改方法、属性或索引器。new 访问修饰符用于显式隐藏继承自基类的成员 抽象类是指至少包含一个抽象成员(尚未实现的方法)的类。抽象类不能实例化 重写方法就是修改基类中方法的实现。virtual 关键字用于修改方法的声明58
  • 59. 总结2-2显式接口实现是用于在名称不明确的情况下确定成员函数实现的是哪一个接口 委托包含对方法而不是方法名称的引用 C# 中的事件允许一个对象将发生的事件或修改通知其他对象59