第十五章 属性
1.属性
属性是一组get/set访问器方法,封装类中的字段和数据。
常规封装字段:
class ApplePie
{
private int sugar;
public int GetSugar()
{
return this.sugar;
}
public void SetSugar(int sugar)
{
this.sugar = sugar;
}
}
public static void Main(string [] args)
{
int sugar = 5;
ApplePie applePie = new ApplePie();
// get访问器读取字段,set访问器设置字段
applePie.SetSugar(sugar);
int getSugar = appplePie.GetSugar();
}
使用get/set方法虽然保护了字段,但也付出了代价。有没有什么方法既可以保护字段,又可以像直接访问字段那样简便的操作?
使用属性:
class ApplePie
{
private int _sugar;
public int Sugar
{
get{ return this._sugar;}
set{ this._sugar = value;} // value为set访问器内置的参数,传递要写入的数据。
// 也可以使用表达式主体方法 =>
//get => return this._sugar;
//set => this._sugar = value;
}
}
public static void Main(string [] args)
{
int sugar = 5;
ApplePie applePie = new ApplePie();
applePie.Sugar = sugar;
applePie.Sugar ++;
}
2.属性的局限性
属性在外观、行为上感觉上都像字段。但属性本质上是方法。此外,属性存在以下限制:
a, 只有在结构或类初始化好之后,才能通过该结构或类的属性来赋值。
ApplePie applePie;
applePie.Sugar = 10; // 编译时错误,applePie未赋值,Sugar本质上是ApplePie的一组get/set方法。
b, 不能将属性作为ref或out参数传给方法;但可写的字段能作为ref或out参数传递。
这是由于属性并不真正指向一个内存位置;相反,它指向的是一个访问器方法。
Bake(ref applePie.Sugar); // 编译时错误
c, 属性最多只能包含一个get和一个set访问器。不能包含其它方法、字段或属性。
d, get和set访问器不能获取任何参数。要赋的值会通过内建的、隐藏的value变量自动传递给set访问器。
e, 不能声明const属性。
const int Flour
{
get => ....
set => .... // 编译时错误
}
3.自动属性
class Pizza
{
public bool Drink { get; set;}
}
// C#编译器自动将这个定义转换成私有字段以及一个默认的实现。换句话说,这个自动属性等价于以下形式:
class Pizza
{
private bool _drink;
public bool Drink
{
get { return this_drink; }
set { this._drink = value; }
}
}
// 虽然形式上等价,但手动写的字段和编译器转换自动属性的私有字段不互通!
class Wine
{
private int _cup;
public int Cup { get;set;}
// 手动关联
public int CupBig
{
get => this._cup;
set => this._cup = value;
}
public void Output()
{
Console.WriteLine($"_cup: {_cup}, Cup: {Cup}");
}
}
public static void Main(string [] args)
{
Wine w = new Wine();
w.Cup = 10;
w.Output();
// 输出:
// _cup: 0, Cup: 10
w.CupBig = 11;
w.Output();
// 输出:
// _cup: 11, Cup: 11
}
4.用属性初始化对象
class Kalakukko // 鱼馅饼
{
private bool _fish;
private bool _sauce;
private bool _beer;
public Kalakukko()
{
this._fish = this._sauce = this._beer = false;
}
public Kalakukko(bool fish)
{
this._fish = fish;
this._sauce = this._beer = false;
}
public Kalakukko(bool fish, bool sauce)
{
this._fish = fish;
this._sauce = sauce;
this._beer = false;
}
public Kalakukko(bool fish, bool sauce, bool beer)
{
this._fish = fish;
this._sauce = sauce;
this._beer = beer;
}
// 如何为鱼馅饼类的字段写不同组合的构造器 ?
// 1.使用可选参数
//public Kalakukko(bool fish = false, bool sauce = false, bool beer = false)
//{
// this._fish = fish;
// this._sauce = sauce;
// this._beer = beer;
//}
}
public static void Main(string [] args)
{
Kalakukko kalakukko = new Kalakukko(true, true);
Kalakukko kalakukko2 = new Kalakukko(true, beer: true); // 传递具名参数(假设使用可选参数)。
}
一个更好、更透明的方式是将私有变量初始化为一组默认值,并将它作为属性公开:
class KalakukkoInProperty
{
private bool _fish = false;
private bool _sauce = false;
private bool _beer = false;
public KalakukkoInProperty(bool fish)
{
this._fish = fish;
}
public bool Fish
{
set => this._fish = value;
}
public bool Sauce
{
set => this._sauce = value;
}
public bool Beer
{
set => this._beer = value;
}
}
public static void Main(string [] args)
{
KalakukkoInProperty kalakukkoIn = new KalakukkoInProperty(true)
{ Fish=true, Sauce = true, Beer = false};
// 先运行构造器,再对属性进行设置
}

浙公网安备 33010602011771号