
Net面向对象程序设计-15-实现属性以访问字段-2010-2011-2.ppt
31页NET面向对象程序设计第15章实现属性以访问字段本章简介Ø使用属性封装逻辑字段 Ø声明get accessor Ø声明set accessor Ø创建声明了属性的接口 Ø使用结构和类实现含有属性的接口 Ø根据字段定义自动生成属性 Ø用属性来初始化对象15.1 使用方法来实现封装Ø 以下结构用于将屏幕上的一个位 置表示成一个(X,Y)坐标对 Ø 假定x坐标的取值范围是0~1280 ,y的取值范围是0~1024private static int rangeCheckedX(int x) { if (x 1280) { throw new ArgumentOutOfRangeException(“X“); } return x; } private static int rangeCheckedY(int y) { if (y 1024) { throw new ArgumentOutOfRangeException(“Y“); } return y; } }struct ScreenPosition { public int X; public int Y; public ScreenPosition(int x, int y) { this.X = rangeCheckedX(x); this.Y = rangeCheckedY(y); } • 问题:违反了封装的原则,没有将其数据保持 private状态 • 虽然ScreenPosition构造器会对它的参数进行范围 检查,但在创建好一个对象后,就可以随便访问 public字段 •ScreenPosition origin = new ScreenPosition(0, 0); •... •int xpos = origin.X; •origin.Y = -100; // oopsØ 解决该问题的方法:将字 段设为private,并添加一 个取值(getr accessor) 方法和一个赋值(set accessor)方法,以便分 别读取和写入每个private 字段的值 Ø 然后,赋值方法就可以对 新的字段值执行范围检查 Ø 缺点:它使用的是不太方 便的,基于方法的语法 uint xpos = origin.GetX(); uorigin.SetX(xpos + 10);struct ScreenPosition { ... public int GetX() { return this.x; } public void SetX(int newX) { this.x = rangeCheckedX(newX); } ... private static int rangeCheckedX(int x) { ... } private static int rangeCheckedY(int y) { ... } private int x, y; }15.2 什么是属性Ø 属性(property)是字段 和方法的一个交集:它看 起来像是一个字段,行为 上又像一个方法 Ø 访问一个属性所用的语法 和访问一个字段的语法是 相同的 Ø 编译器会将这种字段风格 的语法自动转换成对特定 accessor方法的调用Ø 语法:AccessModifier Type propertyName { get { // read accessor code } set { // write accessor code } }struct ScreenPosition { private int x, y; public ScreenPosition(int X, int Y) { this.x = rangeCheckedX(X); this.y = rangeCheckedY(Y); } public int X { get { return this.x; } set { this.x = rangeCheckedX(value); } }public int Y { get { return this.y; } set { this.y = rangeCheckedY(value); } } private static int rangeCheckedX(int x) { ... } private static int rangeCheckedY(int y) { ... } }Ø 注意: u所有set accessor 都用一个隐藏的、 内建的参数来传递 要写入的数据 upublic字段和属性 大写,private的字 段和属性小写Ø属性为访问对象内的实例变量及访问另一个对象 中的实例变量提供了一致性的语法。
Ø属性与等价的存取器方法执行速度同样快 Ø当属性的get语句块和set语句块编译成MSIL时, 它们都转换成方法在MSIL中,get语句块由一 个名字为get_的方法表示 set同样815.2.1 使用属性Ø从ScreenPosition结构的X和Y属性中取值:uScreenPosition origin = new ScreenPosition(0, 0); uint xpos = origin.X; // calls origin.X.get uint ypos = origin.Y; // calls origin.Y.get Ø对属性进行赋值,编译器自动将字段风格的代码 转换成对该属性的set accessor的调用uorigin.X = 40; // calls origin.X.set, with value set to 40 uorigin.Y = 100; // calls origin.Y.Set, with value set to 100 Ø可以通过static关键字声明静态属性,访问static 属性时,要附加类或者结构的名称而不是其实例 的名称作为前缀15.2.2 只读属性Ø 可以声明只包含一个get accessor的属性,即声明了 一个只读属性 Ø X属性不包含set accessor ,所以,如下操作错误 Ø origin.X = 140; // compile- time error示例: struct ScreenPosition { ... public int X { get { return this.x; } } }15.2.3 只写属性Ø 也可以声明只包含一个set accessor的属性,即声明 了一个只写属性 Ø X属性不包含get accessor,所以,如下操 作错误 Console.WriteLine(origin.X); // 编译器error origin.X = 200; // OK origin.X += 10;// 编译器error示例: struct ScreenPosition { ... public int X { set { this.x = rangeCheckedX(value); } } }• 只写属性适用于对密码这 样的属性进行保护15.2.4 属性的可访问性Ø可访问性是在声明属性时指定的,但是,在属性 的声明中,可以为get和set单独指定可访问性struct ScreenPosition { public int X { get { return this.x; } private set { this.x = rangeCheckedX(value); } } public int Y { get { return this.y; } private set { this.y = rangeCheckedY(value); } } private int x, y; }Ø注意: u定义时,只能改变一个accessor的可访问性 uaccessor的访问修饰符所指定的可访问性在限制程度 上必须大于属性的可访问性 Ø警告: u习惯上,为属性和private字段赋予几乎完全相同的名 称,只是首字母大小写有别 u但是,要小心使用class Employee { private int employeeID; public int EmployeeID; { get { return this.EmployeeID; } set { this.EmployeeID = value; } } }15.3 理解属性的局限性Ø属性在外观、行为和感觉上都类似于字段,但它 们本质上是方法,并不是真正的字段 Ø属性的限制: u只有在一个结构或类初始化好之后,才能通过这个结 构或类的属性来进行赋值 ScreenPosition location; location.X = 40; // compile-time error, location not assigned u所以,定义结构和类时,一开始就应该使用属性,而 不是先用字段,后来又变成属性,字段变成属性后, 以前使用了这个类或结构的代码就可能无法正常工作u不能将属性作为一个ref或者out参数值传给一个方法; 但可以将一个可写的字段作为ref或out参数值传递,这是 因为属性并不真正指向一个内存位置,它代表的是一个 方法 u在一个属性中,最多只能包含一个get accessor和一个 set accessor,属性不能包含其他方法、字段或属性 uget accessor和set accessor不能获取任何参数,要赋 的值会通过内建的、隐藏的value变量,自动传给set accessor u不能声明const属性合理使用属性class BankAccount { ... public money Balance { get { ... } set { ... } } private money balance; }class BankAccount { ... public money Balance { get { ... } } public void Deposit(money amount) { ... } public bool Withdraw(money amount) { ... } private money balance; }15.4 在接口中声明属性Ø除了可在接口中声明方法之外,还可以定义属性 interface IScreenPosition { int X { get; set; } int Y { get; set; } } Ø实现该接口的任何类或结构都必须实现X和Y属性 ,并在属性中定义get和setstruct ScreenPosition : IScreenPosition { ... public int X { get { ... } set { ... } } public int Y { get { ... } set { ... } } ... }Ø在类中实现接口属性时,可以将属性的实现声明 为virtual,以允许未来派生的类重写这些实现 Ø例如:class ScreenPosition : IScreenPosition { public virtual int X { get { ... } set { ... } } public virtual int Y { get { ... } set { ... } } }Ø还可以选择使用显式接口实现语法来实现一个属 性,属性的显式实现是非公共和非虚的(因而不 能被重写)struct Scre。












