第十五章、实现属性以访问字段

  属性既维持了封装性,又可以使用字段风格的语法。

  什么是属性

  属性是字段和方法的交集——看起来像字段,用起来像方法。访问属性所使用的语法和访问字段相同。然而,编译器会将这种风格的语法自动转换成对特定访问器方法的调用。属性的声明如下所示:

  访问修饰符 类型 属性名

  {

  get

  {

  //取值代码

  }

  set

  {

  //赋值代码

  }

  }

  属性可以包含两个代码块,分别以get和set关键字开头。其中,get块包含读取属性时执行的语句,set块包含向属性写入时执行的语句。属性的类型指定了由get和set访问器读取和写入的数据的类型。

  所有set访问器都用一个隐藏的、内建的参数(名为value)来传递要写入的数据。

  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){.......}

  }

  属性唯一要求的就是由get访问器返回指定类型的值。值还能动态计算获得,不一定要从存储好的数据中获取。

  使用属性

  在表达式中使用属性时,要么取值,要么赋值。

  ScreenPosition origin = new  ScreenPosition(0,0);

  //取值,调用后get访问器

  int xpos = origin.X //实际调用origin.X.get

  int ypos = origin.Y //实际调用origin.Y.get

  //赋值,调用set访问器

  origin.X = 40;  //实际

       origin.X += 40; //同时取值和赋值,get和se调用origin.X.set , value = 40

  origin.Y = 100;  //实际调用origin.Y.set , value = 100t访问器都会被调用

  只读属性:可以声明只包含get访问器的属性,这称为只读属性。只读属性不包含set访问器,对其进行写入操作会报告编译错误。

  只写属性:类似的,可声明只包含set访问器的属性,这称为只写属性。只写属性不包含get访问器,对其进行读取操作会报告编译错误。只写属性适用于对密码这样的数据进行保护。

  属性在外观、行为和感觉上都像字段。但属性本质上是方法而不是字段。另外,属性存在以下限制:

  1、只有在结构或类初始化好之后,才能通过该结构或类的属性来赋值。

  ScreenPosition  location;

  location.X = 40;//编译错误,location尚未赋值

  2、不能将属性作为ref和out参数传给方法;但可写的字段能作为ref和out参数值传递。

  MyMethod(ref location.X);  //编译时错误

  3、属性中最多只能包含一个get和一个set访问器。不能包含其他方法、字段或属性。

  4、get和set访问器不能获取任何参数。要赋的值会通过内建的、隐藏的value变量自动传给set访问器。

  5、不能声明const属性。

  const int X{ get{...} set{...} }//编译错误

  在接口中声明属性

  除了可以在接口中定义方法之外,还可以定义属性。为此,需要指定get或set关键字,或者同时指定这两个关键字。但要将get或set访问器的主体替换成分号。如下:

  interface IScreenPosition

  {

  int X {get;set;} //没有修饰符

  int Y{get;set;} //没有修饰符

  }

  实现这个接口的任何类或结构都必须实现X和Y属性,并在属性中定义get和set访问器。如下:

  struct ScreenPosition: IScreenPosition

  {

  .............

  public int X

  {

  get{....}

  set{.....}

  }

  public int Y

  {

  get{....}

  set{.....}

  }

  ..............

  }

  在类中实现接口的属性时,可将属性的实现声明为virtual,从而允许派生类重写实现。如下:

  struct ScreenPosition: IScreenPosition

  {

  .............

  public virtual  int X

  {

  get{....}

  set{.....}

  }

  public virtual  int Y

  {

  get{....}

  set{.....}

  }

  ..............

  }

  还可使用显示接口实现语法来实现属性。属性的显示实现是非公共和非虚的(因而不能被重载)。

  生成自动属性

  C#编译器现在能自动为属性生成代码,如下:

  class Circle{

  public int Radius{ get; set; }

  ..........

  }

  在这个例子中,Circle类包含名为Radius的属性。除了属性的类型,不必指定这个属性是如何工作的——get和set访问器都是空白的。C#编译器自动将这个定义转换成私有字段以及一个默认的实现,如下所示:

  class Circle{

  private int _radius;

  public int Radius{

  get{ return this. _radius;}

  set{ this. _radius=value;}

  }

  .........

  }

  定义自动属性时,语法与在接口中定义属性几乎完全相同。区别在于,自动属性可以指定访问修饰符,例如private,public,或者protect。

  使用属性来初始化对象

  public class Triangle

  {

  private int side1Length = 10;

  private int side2Length = 10;

  private int side3Length = 10;

  public int side1Length

  {

  set{this. side1Length  = value;}

  }

  public int side2Length

  {

  set{this. side2Length  = value;}

  }

  public int side3Length

  {

  set{this. side3Length  = value;}

  }

  }

  创建类的实例时,可以提供了set访问器的任何公共属性指定名称和值。如下:

  Triangle tri1= new Triangle{side3Length  = 15;}

  Triangle tri2= new Triangle{side1Length  = 15;side3Length  = 20;}

  Triangle tri3= new Triangle{side2Length  = 15;side3Length  = 20;}

  Triangle tri4= new Triangle{side1Length  = 15;side2Length  = 15;side3Length  = 20;}

  这个语法称为对象初始化器。像这样调用对象初始化器,C#编译器会自动生成代码来调用默认构造器,然后调用每个具名属性的set访问器,把它初始化成指定值。还可将对象初始化器与非默认构造器配合使用。例如,假定Triangle类还有一个构造器能获取单个字符串参数(描述三角形的类型),那么可以调用这个构造器,同时对其他属性进行初始化;

  Triangle tri4= new Triangle("等边三角形"){side1Length  = 15;side2Length  = 15;side3Length  = 15;}

  对象初始化器与自动属性配合使用

  Polygon类

  using System;

  using System.Collections.Generic;

  using System.Linq;

  using System.Text;

  using System.Threading.Tasks;

  namespace AutomaticProperties

  {

  class Polygon

  {

  public int NumSides { get; set; }

  public double SideLength { get; set; }

  public Polygon()

  {

  this.NumSides = 4;

  this.SideLength = 10.0;

  }

  }

  }

  Program类

  using System;

  using System.Collections.Generic;

  using System.Text;

  namespace AutomaticProperties

  {

  class Program

  {

  static void doWork()

  {

  Polygon square = new Polygon();

  Polygon triangle = new Polygon { NumSides = 3 };

  Polygon pentagon = new Polygon { SideLength = 15.5, NumSides = 5 };

  Console.WriteLine("Square: number of sides is {0}, length of each side is {1}",

  square.NumSides, square.SideLength);

  Console.WriteLine("Triangle: number of sides is {0}, length of each side is {1}",

  triangle.NumSides, triangle.SideLength);

  Console.WriteLine("Pentagon: number of sides is {0}, length of each side is {1}",

  pentagon.NumSides, pentagon.SideLength);

  }

  static void Main(string[] args)

  {

  try

  {

  doWork();

  }

  catch (Exception ex)

  {

  Console.WriteLine(ex.Message);

  }

  }

  }

  }

  

原文地址:https://www.cnblogs.com/linhuide/p/5819885.html