字段和属性(C#)

时间:2022-03-13 14:55:58

类成员默认为私有成员。如果不为类成员添加访问修饰符,那么默认的是private。

公共成员需要显式指定。


字段和属性是不同的两个概念:

字段是类中实打实的一种变量。用来存储与对象相关联的数据。所以当字段规定为public时,在当前类之外其他类中

时随地访问和修改字段值(只要定义好当前类的实例即可,除static外),也就是类中所有的细节都能看的很清

楚。这符合封装的思想,但却不是彻底的封装,因为它并不安全。

所以我们又可以规定字段的访问修饰符为private,这样只有在当前类中可以访问,在类之外的其他类就访问和修改不

到了。


但是以上又引申出两个方面的问题:

1.private封装的太过彻底,public开放的太大,而当我们需要规定字段只读或者只写怎么办?

2.我们可以在类之外看到类中有哪些字段,知道类中具体的字段名是什么,这不符合安全原则。


为了解决以上问题,当然可以将所有字段设置为private,再专门为每个字段提供公共取值和赋值方法用于访问和修改

它们的值。但是这样的代价是我们无法再使用赋值操作符设置类中的字段数据(即我们不能用类的实例.字段名 =

值来设定),必须调用对应的方法来访问修改字段数据。


因此才最终推导出属性这个东西。

C#本质论的书上说,属性的关键特点是,提供了从编程角度看类似于字段的API(个人认为这句话最为贴切属性的作用

和含义)。

不懂属性到底是什么或者只知道用却不去深究其与字段关系的,看到这句话多少会有些豁然开朗的感觉。


属性有哪些好处呢:

1.解决直接能看出类中字段名的弊端。类的字段名如果是_PhoneNumber,那么对应的属性名可以定义为PhoneNumber。

我们在类外的其他类中,可以通过类的实例.PhoneNubmer来进行赋值和访问字段数据,但是其实真正的字段名是_PhoneNumber,但事实上调用的人并不知道。

2.我们又可以用赋值操作符进行访问和修改类的成员数据。

3.定义只读只写属性更为方便。

4.利用属性可以提供属性验证,在将value赋给字段之前,可以拦截赋值,先对其进行验证。


在实际编程过程中,要一直使用属性,不要直接调用字段。要不然属性还有毛线意义。

另外属性不能作为ref和out参数。

对于只读属性,利用类构造器依旧不可以对其进行赋值,只能通过字段初始化赋值。

属性可以作为虚字段使用。即可以让属性取值方法返回计算好的值,让赋值方法解析值,解析的值存到其他成员字段

中,而属性本身并没有实质的意义。


请看下面一个例子:

public PropertyExample()
{
this.InitializeComponent();

Phone phone = new Phone();

phone.Brand = "Microsoft";
txtblk1.Text = string.Format("手机品牌是:{0}",phone.Brand);
phone.PhoneNubmer = "123456789";
txtblk2.Text = string.Format("手机号码是:{0}", phone.PhoneNubmer);

//错误,无法为属性PhoneType赋值,因为它是只读的
//对于只读属性,我们只能利用字段对其进行赋初始值
//phone.PhoneType = "Lumia 950XL";
txtblk3.Text = string.Format("手机型号是:{0}", phone.PhoneType);

phone.PhoneSDSize = "64GB";
//错误,因为属性PhoneSDSize是只写的,不能读取值;所以就功能上来说只写的情况是很罕见的
//只写的属性一般你进行了赋值却不能最终输出获取到属性值,所以比较少见
//txtblk3.Text = string.Format("手机SD卡容量:{0}", phone.PhoneSDSize);

//虽然给属性PhonePrice设置了get,set,但是set用private修饰符修饰,那么在类的外部set访问器便不可访问
//phone.PhonePrice = "3599";
phone.MakePrice("3599");
txtblk4.Text = string.Format("手机价格是:{0}",phone.PhonePrice);

txtblk5.Text = string.Format("手机屏幕尺寸:{0}", phone.PhoneScreenSize);
phone.PhoneScreenSize = "5.7";
txtblk6.Text = string.Format("修改后的手机屏幕尺寸:{0}", phone.PhoneScreenSize);
}

//以下属性只用于说明问题,与属性字面意义并无挂钩,无需纠结
class Phone
{

//属性第一种定义方法
private string _Brand;
public string Brand
{
get
{
return _Brand;
}
set
{
_Brand = value;
}
}

//属性第二种定义方法
public string PhoneNubmer { get; set; }

//只读属性(只能通过赋初始值的方式赋值,并不能通过类构造器赋值)
private string _PhoneType = "Lumia 1320";
public string PhoneType
{
get
{
return _PhoneType;
}
}

//只写属性
private string _PhoneSDSize;
public string PhoneSDSize
{
set
{
_PhoneSDSize = value;
}
}

//为属性的取值方法和赋值方法指定访问修饰符
//赋值方法指定private修饰符后,PhonePrice属性在Phone类内部是可读可写的,在Phone类之外只读
private string _PhonePrice;
public string PhonePrice
{
get
{
return _PhonePrice;
}
private set
{
_PhonePrice = value;
}
}

//因为类的所有成员包括方法,都是默认私有的
//所以在要决定价格的函数必须明确指定为public才能在类的外部被调用
//也可以利用类的构造器对属性进行赋值,因为在类内部属性PhonePrice可读可写(跟只读属性比较)
public void MakePrice(string price)
{
//注意这边也可以写_PhonePrice = price;
//但是出于需要优先使用属性的原则,不要直接调用字段进行赋值
PhonePrice = price;
}

//对字段进行初始化赋值
private string _PhoneScreenSize = "5.5";
public string PhoneScreenSize
{
get
{
return _PhoneScreenSize;
}
set
{
_PhoneScreenSize = value;
}
}

}

结果截图:

字段和属性(C#)