属性允许将数据成员作为公共接口的一部分暴露出去,同时仍旧提供面向对象环境下所需要的封装。属性这个语言元素可以让你像访问数据成员一样使用,但底层依旧使用方法实现。通过使用属性我们可以创建出类似于数据访问(客户代码在访问属性时,就像是在访问共有的字段),但实际上确是方法调用的接口,自然也可以享受到方法调用的所有好处。
在.NET Framework中,数据绑定类仅支持属性,而不支持公有数据成员。对于所有的数据绑定类库均是如此,包括WPF、Windows Forms和Silverlight。
关于数据绑定:
数据绑定就是将某个对象的一个属性和某个用户界面控件相互关联起来。数据绑定机制使用反射来找到类型中的特定属性
下面的代码表示:将txt_City控件的Text属性绑定到了Address对象的City属性上。
this.txt_City.DataBindings.Add("Text", address, "City");
使用属性使我们可以更容易应对变化,当日后产生新的需求或行为时,属性更加易于修改。假如,客户对象不应该有空白的名称。若使用的公有属性来封装Name,那么只需要这样简单的修改即可:
1 public class Customer 2 { 3 private string name; 4 public string Name 5 { 6 get { return name; } 7 set 8 { 9 if (string.IsNullOrEmpty(value)) 10 { 11 throw new ArgumentException("不能使用空白名称", "Name"); 12 } 13 name = value; 14 } 15 } 16 }
属性的多线程支持
因为属性是通过方法来实现的,所以添加多线程支持也非常简单。只需下面的简单修改可以支持对数据的同步访问:
1 public class Customer 2 { 3 private object syncHandle = new object(); 4 5 private string name; 6 public string Name 7 { 8 get 9 { 10 lock (syncHandle) 11 return name; 12 } 13 set 14 { 15 if (string.IsNullOrEmpty(value)) 16 { 17 throw new ArgumentException("不能使用空白名称", "Name"); 18 } 19 lock (syncHandle) 20 name = value; 21 } 22 } 23 }
属性是通过方法来实现的,那么自然属性也拥有方法的所有语言特征(abstract,Interface,virtual...)
属性可以定义为virtual的:
1 public class Customer 2 { 3 //C#3.0隐式属性语法(不需要验证getter或setter逻辑时) 4 public virtual string Name 5 { 6 get; 7 set; 8 } 9 }
也可以将属性声明为抽象(abstract)的,以类似隐式属性语法的形式定义在接口中。接口只是定义了一个契约,强制所以实现了该接口的类型都必须满足,如下:
1 public interface INameValuePair<T> 2 { 3 string Name 4 { 5 get; 6 } 7 8 T Value 9 { 10 get; 11 set; 12 } 13 }
属性的访问器将作为两个独立的方法编译到你的类型中,我们可以为属性的get和set访问器制定不同的访问权限以实现更精确的控制:
1 public class Customer 2 { 3 public virtual string Name 4 { 5 get; 6 protected set; 7 } 8 }
通过属性返回序列中的项,我们可以看到List<T>类的Item属性(所有得索引器都是使用this关键字声明的):
1 public T this[int index] 2 { 3 get; 4 set; 5 }
小节:
无论何时需要在类型的公有或保护接口中暴露数据,都应该使用属性。你也应该使用索引器来暴露序列或字典。所有的数据成员都应该是私有的,没有任何例外。这样你就立即得到了数据绑定的支持,也便于日后对方法实现进行各种修改。
阅读书目:《Effective C#》