第六章 属性、索引器、委托和事件
回顾 继承是获得现有类的功能的过程 创建新类所根据的基础类称为基类或父类,新建的类则称为派生类或子类 base 关键字用于从派生类中访问基类成员 override 关键字用于修改方法、属性或索引器。new 访问修饰符用于显式隐藏继承自基类的成员 抽象类是指至少包含一个抽象成员(尚未实现的方法)的类。抽象类不能实例化 重写方法就是修改基类中方法的实现。virtual 关键字用于修改方法的声明 显式接口实现是用于在名称不明确的情况下确定成员函数实现的是哪一个接口
目标 理解属性及其不同的类型、实现 理解和使用索引器 实现委托 定义和触发事件
属性简介 3-1 class Employee { private static string _name; private static string _id; static void Main(string[] args) _name = Console.ReadLine(); _id = Console.ReadLine(); } 直接访问字段 不经验证
属性简介 3-2 属性 … Employee emp; emp.SetId("A1"); class Employee string Department = emp.Get() class Employee { private static string _name; private static string _id; public void SetId(value) // 验证输入长度小于 2 if (_id.Length > 2) _id = value; } public string GetId() return _id; 每次都调用 GetId() 和 SetId() 方法会很繁琐 属性 方法 SetId(Value) 和 GetId() 分别读取和写入职员 ID
属性简介 3-3 class Employee { private static string _name; private static string _id; public string Id get return _id; } set // 验证输入长度小于 2 if (_id.Length > 2) _id = value; 读取 ID 时调用 将值赋给 ID 时调用
属性类型 4-1 读/写属性 [访问修饰符] 数据类型 属性名 { get{ }; set{ }; } 可以赋值和检索值
属性类型 4-2 只读属性 [访问修饰符] 数据类型 属性名 { get{ }; } 只能检索值
属性类型 4-3 只写属性 [访问修饰符] 数据类型 属性名 { set{ }; } 只能赋值
属性类型 4-4 应用于整个类而不是类的实例 静态属性 [访问修饰符]static 数据类型 属性名 { get{}; set{}; } 只能访问类的静态成员 应用于整个类而不是类的实例
定义和调用属性 4-1 演示…… 只读属性 class SavingsAccount { // 类字段用于存储帐号、余额和已获利息 private int _accountNumber; private double _balance; private double _interestEarned; // 利率是静态的,因为所有帐户获得的利息相同 private static double _interestRate; // 构造函数初始化类成员 public SavingsAccount(int accountNumber, double balance) this._accountNumber = accountNumber; this._balance = balance; } // 只读 AccountNumber 属性 public int AccountNumber get return _accountNumber; 只读属性 演示……
定义和调用属性 4-2 将设置 InterestEarned 属性 static void Main(string[] args) { // 创建 SavingsAccount 的对象 SavingsAccount objSavingsAccount = new SavingsAccount(12345, 5000);; Console.WriteLine("输入到现在为止已获得的利息和利率"); objSavingsAccount.InterestEarned = Int64.Parse(Console.ReadLine()); SavingsAccount.InterestRate = objSavingsAccount.InterestEarned += objSavingsAccount.Balance * SavingsAccount.InterestRate; Console.WriteLine("获得的总利息为: {0}", objSavingsAccount.InterestEarned); } public double InterestEarned { get return _interestEarned; } set // 验证数据 if (value < 0.0) Console.WriteLine(“利息 不能为负数"); return; _interestEarned = value; 将设置 InterestEarned 属性
定义和调用属性 4-3 将设置 InterestRate 属性 static void Main(string[] args) { // 创建 SavingsAccount 的对象 SavingsAccount objSavingsAccount = new SavingsAccount(12345, 5000);; Console.WriteLine("输入到现在为止已获得的利息和利率"); objSavingsAccount.InterestEarned = Int64.Parse(Console.ReadLine()); SavingsAccount.InterestRate = objSavingsAccount.InterestEarned += objSavingsAccount.Balance * SavingsAccount.InterestRate; Console.WriteLine("获得的总利息为: {0}", objSavingsAccount.InterestEarned); } public static double InterestRate { get return _interestRate; } set // 验证数据 if (value < 0.0) Console.WriteLine(“利率不能为负数"); return; else _interestRate = value / 100; 将设置 InterestRate 属性
将检索 Balance 和 InterestRate 属性 定义和调用属性 4-4 static void Main(string[] args) { // 创建 SavingsAccount 的对象 SavingsAccount objSavingsAccount = new SavingsAccount(12345, 5000);; Console.WriteLine(“输入到现在为止已获得的利息和利率"); objSavingsAccount.InterestEarned = Int64.Parse(Console.ReadLine()); SavingsAccount.InterestRate = objSavingsAccount.InterestEarned += objSavingsAccount.Balance * SavingsAccount.InterestRate; Console.WriteLine("获得的总利息为: {0}", objSavingsAccount.InterestEarned); } public double Balance { get if (_balance < 0) Console.WriteLine("没有可用余额"); return _balance; } 将检索 Balance 和 InterestRate 属性
索引器 语法 [访问修饰符] 数据类型 this[数据类型 标识符] { get{}; set{}; }
定义和调用索引器 4-1 演示…… 以 Title 属性表示照片 将照片存放于数组 photos 中 class Photo { string _title; public Photo(string title) this._title = title; } public string Title get return _title; class Album { // 该数组用于存放照片 Photo[] photos; public Album(int capacity) photos = new Photo[capacity]; } 演示……
定义和调用索引器 4-2 带有 int 参数的 Photo 索引器 读/写索引器 public Photo this[int index] { get // 验证索引范围 if (index < 0 || index >= photos.Length) Console.WriteLine("索引无效"); // 使用 null 指示失败 return null; } // 对于有效索引,返回请求的照片 return photos[index]; set return; photos[index] = value; 定义和调用索引器 4-2 带有 int 参数的 Photo 索引器 读/写索引器
定义和调用索引器 4-3 带有 string 参数的 Photo 索引器 只读索引器 public Photo this[string title] { get // 遍历数组中的所有照片 foreach (Photo p in photos) // 将照片中的标题与索引器参数进行比较 if (p.Title == title) return p; } Console.WriteLine("未找到"); // 使用 null 指示失败 return null; 只读索引器
定义和调用索引器 4-4 static void Main(string[] args) { // 创建一个容量为 3 的相册 Album friends = new Album(3); // 创建 3 张照片 Photo first = new Photo("Jenn"); Photo second = new Photo("Smith"); Photo third = new Photo("Mark"); // 向相册加载照片 friends[0] = first; friends[1] = second; friends[2] = third; // 按索引检索 Photo obj1Photo = friends[2]; Console.WriteLine(obj1Photo.Title); // 按名称检索 Photo obj2Photo = friends["Jenn"]; Console.WriteLine(obj2Photo.Title); }
委托 委托和方法必须具有相同的签名 在运行时确定调用哪种方法 Multiply(int,int) { …. } --- Divide(int,int) --- public delegate Call(int num1, int num2); 委托和方法必须具有相同的签名
[访问修饰符] delegate 返回类型 委托名(); 定义委托 2-1 class Delegates { public delegate int Call(int num1, int num2); class Math public int Multiply(int num1, int num2) // 实现 } public int Divide(int num1, int num2) {// 实现 class TestDelegates static void Main() Call objCall; Math objMath = new Math(); objCall = new Call(math.Multiply); 语法 [访问修饰符] delegate 返回类型 委托名(); 将方法与委托关联起来
定义委托 2-2 将方法与委托关联起来 class Delegates { // 委托定义 public delegate int Call(int num1, int num2); class Math // 乘法方法 public int Multiply(int num1, int num2) return num1*num2; } // 除法方法 public int Divide(int num1, int num2) return num1/num2; 定义委托 2-2 将方法与委托关联起来 static void Main(string[] args) { // 委托的对象 Call objCall; // Math 类的对象 Math objMath = new Math(); // 将方法与委托关联起来 objCall = new Call(objMath.Multiply); // 将委托实例化 result = objCall(5, 3); System.Console.WriteLine("结果为 {0}", result); }
事件 不关心 事件的发布者 “请听题~” 其他人 抢答者 抢答者 事件源 定义事件 宣布人 为对象订阅该事件 将发生的事件通知给订阅人 集中注意力聆听 未订阅该事件 其他人 抢答者 抢答者 事件的订阅人
定义事件 public delegate void delegateMe(); 语法 [访问修饰符] event 委托名 事件名; public delegate void delegateMe(); private event delegateMe eventMe;
订阅事件 eventMe += new delegateMe(objA.Method); eventMe += new delegateMe(objB.Method);
通知订阅对象 if(condition) { eventMe(); } 调用订阅特定事件的对象的所有委托
示例 演示:示例 4 class ClassA class Delegate class TestEvents { { { // 定义委托 public void DispMethod() Console.WriteLine(“Class A 已接到 NotifyEveryOne 事件的通知!"); } // 第二个类 class ClassB Console.WriteLine(“Class B 已接到 NotifyEveryOne 事件的通知! "); class Delegate { // 定义委托 public delegate void MeDelegate(); // 定义事件 public event MeDelegate NotifyEveryOne; public void Notify() // 如果事件不为 null if(NotifyEveryOne != null) Console.WriteLine("触发事件:"); // 触发事件 NotifyEveryOne(); } 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); new Delegate.MeDelegate(objClassB.DispMethod); objDelegate.Notify(); } 演示:示例 4
总结 属性通过使用访问器读取和写入类中的字段,对字段进行保护 属性分类为以下四种不同的类型: 读/写属性 只读属性 只写属性 可以在类中定义索引器,允许使用下标对该类的对象中的数据进行访问 索引器必须总是命名为 this,因为对它们的访问是通过其所属的对象进行的 委托包含对方法而不是方法名称的引用 C# 中的事件允许一个对象将发生的事件或修改通知其他对象