参考书:《 visual C# 从入门到精通》
第三部分 用C#定义可扩展类型
第15章 实现属性以访问字段
文章目录
15.1 使用方法实现封装
如下面的例子
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;
}
这样的话要改变X
的值应如下:
ScreenPosition orgin=new ScreenPosition(0,0);
int xpos=orgin.GetX();
orgin.SetX(xpos+10);
15.2 什么是属性
属性
是字段和方法的交集。属性的声明如下:
AccessModifier Type ProperName{
get{
//取值代码
}
set{
//赋值代码
}
}
如下代码用属性改写ScreenPosition
的结构,注意所有的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){...;}
}
15.2.1 使用属性
使用属性时,要么从之取值要么向其辅助:
ScreenPosition origin=new ScreenPosition();
int xpos=origin.X;//实际调用origin.X.get
int ypos=origin.Y;
origin.X=40;//实际调用origin.X.get. value设为40
origin.Y=100;
origin.X+=10;//同时对属性进行取值和赋值,get和set访问器都被用到了
15.2.2 只读属性
Struct ScreenPosition{
private int _x;
...;
public int X{
get{return this._x;}
}
}
15.2.3 只写属性
struct ScreenPosition{
private int _x;
...;
public int X{
set{this._x=rangeCheckedX(value);}
}
}
15.2.4 属性的可访问性
在属性声明中可以为get
和set
访问器单独指定可访问性,这样可以覆盖属性的可访问性。如:
struct ScreenPosition{
private int _x,_y;
...;
public int X{
get{return thix._x;}
private set{this._x=rangeCheckedX(value);}
}
...;
}
注意:
- 只能改变一个访问器的可访问性,改变两个是没有意义的
- 访问器的访问修饰符指定的可访问性在限制程度上必须大于属性的可访问性。如属性设为私有,访问器不能设为公共
15.3 理解属性的局限性
属性本质上是方法而不是字段,所以它存在以下的限制:
- 只有在结构或类初始化后才能通过属性来赋值
- 不能将属性作为
ref
或out
参数值传给方法 - 属性最多只能包含一个
get
和set
访问器 - 访问器不能货期任何参数
- 不能声明
const
属性
15.4 在接口中声明属性
如:
interface IScreenPostion{
int X{get;set;}
int Y{get,set;}
}
在类中实现接口的属性时可将属性的实现声明为virtual
,从而允许派生类重写实现,如:
class ScreenPositon:IScreenPosition{
...;
public virtual int X{
get{...;}
set{...;}
}
public virtual int Y{
get{...;}
set{...;}
}
...;
}
也可以用显式接口实现语法来来实现属性。属性的显式实现时非公共的且非虚的。如:
class ScreenPositon:IScreenPosition{
...;
int IScreenPosition.X{
get{...;}
set{...;}
}
int IScreenPosition.Y{
get{...;}
set{...;}
}
...;
}
15.5 生成自动属性
属性的价值主要体现在以下几点:
- 字段和属性的读写语法相同,但本质上是完全不同的,意味着当你觉得有必要时将字段变为属性可以方便一些
- 属性与接口的兼容性
C#编译器现在能自动为属性生成代码,如下:
class Circle{
public int Radius{get;set;}
...;
}
编译器自动将这个定义转换为私有字段和一个默认的实现:
class Circle{
public int Radius{
get{
returen this._radius;
}
set{
this._radius=value;
}
}
...;
}
15.6 用属性初始化对象
创建类的实例时可为具有set
访问器的任何公共属性指定名称和值。如:
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;}
}
}
这样在初始化时可对三个边的任意组合初始化:
Triangle tri1=new Triangle{Side3Length=15}
这种语法称为对象初始化器或集合初始化列表。编译器会自动生成代码来调用默认构造器,然后调用每个具名属性的set
访问器,把它初始化成指定值。
如下面的一个实例:
Polygon.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace C_15_6
{
class Polygon
{
public int NumSides { get; set;}
public double SideLength { get; set; }
public Polygon()
{
this.NumSides = 4;
this.SideLength = 10.0;
}
}
}
Program.cs
using System;
namespace C_15_6
{
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 {square.NumSides}," +
$" length of each side is{triangle.SideLength}");
Console.WriteLine($"Triangle: number of side is {triangle.NumSides}, " +
$"length of each is {triangle.SideLength}");
Console.WriteLine($"Pentagon: number of side is {pentagon.NumSides}, " +
$"length of each is {pentagon.SideLength}");
}
static void Main(string[] args)
{
dowork();
}
}
}