条款一 : 使用属性代替可访问的数据成员
在c#中,属性(property)是这样一种语言元素,它们在被访问的时候看起来好像是数据成员,但是它们却是用方法实现的。
有时候,一些类型成员最好的表示形式就是数据。例如一个客户的名字。使用属性我们可以创建一种特殊的接口------这种接口在行为上像数据访问,但却仍能获得函数的全部好处。客户代码对属性的访问就像访问公有变量一样。但实际的实现采用的却是方法,这些方法内部定义了属性访问器的行为。
.NET框架中的数据绑定类只支持属性,而不支持公有数据成员,这些数据绑定类会将对象的属性关联到用户界面控件上。其数据绑定机制事实上是使用反射来查找一个类型中具有特定名称的属性。例如:
textBoxCity.DataBindings.Add("Text",address,"City");
便是将textBoxCity控件的Text属性和address对象的City属性绑定在一起。
当然,数据绑定类所应有的类一般都要和用户界面打交道。但这不意味着属性只在UI逻辑中有用武之地。对于其它类的结构,我们也需要使用属性。随着时间的推移,新的需求或行为往往会影响原来类型的实现,采用属性比较容易能够应对这些变化。例如,我们可能很快会发现Customer类不能 有一个空的Name。如果我们使用一个公开属性来实现Name,那么只需要在一个地方做更改即可。
2{
3 private string _name;
4 public string Name
5 {
6 get
7 {
8 return _name;
9 }
10 set
11 {
12 if(value == null || value.Length == 0)
13 throw new ArgumentException("Name cannot be blank", "Name");
14 _name = value;
15 }
16 }
17}
如果使用的是公有数据成员,我们就要寻找并修改所有设置Customer的Name的代码,那将花费大量的时间。
另外,由于属性是采用方法来实现的,因此为它们添加多线程支持就更加容易---直接在get和set方法中提供同步数据访问控制。
{
get
{
lock (this)
return _name;
}
set
{
lock (this)
return _name;
}
}
既然是采用方法来实现的,那么属性也就具有了方法所具有的全部功能。比如,属性可以实现为虚属性:
{
private string _name;
public virtual string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
}
自然,属性也可以实现为抽象属性,或者作为接口定义的一部分:
{
object Name
{
get;
}
object Value
{
get;
set;
}
}
最后,我们还可以借助属性的特点来创建const和非const版本的接口:
{
object Name
{
get;
}
object Value
{
get;
}
}
public interface INameValuePair
{
object Value
{
get;
set;
}
}
public class Stuff : IConstNameValuePair,INameValuePair
{
private string _name;
private object _value;
IConstNameValuePair Members
INameValuePair
}
属性在C#中已经成为一项比较完善的、第一等的语言元素。我们可以针对成员函数做的任何事情,对于属性也同样适用。毕竟,属性是对访问/修改内部数据的方法的一种扩展。
我们知道,属性访问器在编译后事实上是两个分离的方法。在C#2.0种,我们可以为一个属性的get访问器和set访问器指定不同的访问修饰符。这使得我们可以更好的控制属性的可见性。
{
private string _name;
public virtual string Name
{
get
{
return _name;
}
protected set
{
_name = value;
}
}
}
C#的属性语法扩展自简单的数据字段。如果类型接口需要包含一些索引数据项,则可以使用一种称作索引器(indexer)的类型成员。索引器在C#中又称含参属性(parameterized property)。这种“使用数下来返回一个序列中的数据项”的做法对于很多场合非常有用,下面的代码展示了这一用法:
{
get
{
return _theValues[index];
}
set
{
_theValues[index] = value;
}
}
综上所述,只要打算将数据暴露在类型的公有接口或受保护接口中,我们都应该使用属性来实现。对于具有序列或者字典特征的类型,则应该采用索引器。所有的数据成员都应该一律声明为私有。使用属性的好处显而易见:我们可以得到更好的数据绑定支持,我们可以更容易的在将来对其访问访问方法的实现做任何改变。