- 1、本文档共75页,可阅读全部内容。
- 2、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
- 3、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载。
- 4、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
查看更多
改善C程序的50种
为什么程序已经可以正常工作了,我们还要改变它们呢?答案就是我们可以让它们变得更好。我们常常会改变所使用的工具或者语言,因为新的工具或者语言更富生产力。如果固守旧有的习惯,我们将得不到期望的结果。对于C#这种和我们已经熟悉的语言(如C++或Java)有诸多共通之处的新语言,情况更是如此。人们很容易回到旧的习惯中去。当然,这些旧的习惯绝大多数都很好,C#语言的设计者们也确实希望我们能够利用这些旧习惯下所获取的知识。但是,为了让C#和公共语言运行库(Common Language Runtime,CLR)能够更好地集成在一起,从而为面向组件的软件开发提供更好的支持,这些设计者们不可避免地需要添加或者改变某些元素。本章将讨论那些在C#中应该改变的旧习惯,以及对应的新的推荐做法。
条款1:使用属性代替可访问的数据成员
C#将属性从其他语言中的一种特殊约定提升成为一种第一等(first-class)的语言特性。如果大家还在类型中定义公有的数据成员,或者还在手工添加get和set方法,请赶快停下来。属性在使我们可以将数据成员暴露为公有接口的同时,还为我们提供了在面向对象环境中所期望的封装。在C#中,属性(property)是这样一种语言元素:它们在被访问的时候看起来好像是数据成员,但是它们却是用方法实现的。
有时候,一些类型成员最好的表示形式就是数据,例如一个客户的名字、一个点的x/y坐标,或者上一年的收入。使用属性我们可以创建一种特殊的接口——这种接口在行为上像数据访问,但却仍能获得函数的全部好处。客户代码 HYPERLINK /bookfiles/295/10029512579.shtml \l _ftn1 \o [1]对属性的访问就像访问公有变量一样。但实际的实现采用的却是方法,这些方法内部定义了属性访问器的行为。
.NET框架假定我们会使用属性来表达公有数据成员。事实上,.NET框架中的数据绑定类只支持属性,而不支持公有数据成员。这些数据绑定类会将对象的属性关联到用户界面控件(Web控件或者Windows Forms控件)上。其数据绑定机制事实上是使用反射来查找一个类型中具有特定名称的属性。例如下面的代码:
textBoxCity.DataBindings.Add(Text,?????? address, City);
便是将textBoxCity控件的Text属性和address对象的City属性绑定在一起。(有关数据绑定的细节,参见条款38。)如果City是一个公有数据成员,这样的数据绑定就不能正常工作。.NET框架类库(Framework Class Library)的设计者们之所以不支持这样的做法,是因为将数据成员直接暴露给外界不符合面向对象的设计原则。.NET框架类库这样的设计策略从某种意义上讲也是在推动我们遵循面向对象的设计原则。对于C++和Java编程老手,我想特别指出的是这些数据绑定代码并不会去查找get和set函数。在C#中,我们应该忘掉get_和set_这些旧式的约定,而全面采用属性。
当然,数据绑定所应用的类一般都要和用户界面打交道。但这并不意味着属性只在UI(用户界面)逻辑中有用武之地。对于其他类和结构,我们也需要使用属性。随着时间的推移,新的需求或行为往往会影响原来类型的实现,采用属性比较容易能够应对这些变化。例如,我们可能很快就会发现Customer类型不能有一个空的Name。如果我们使用一个公用属性来实现Name,那么只需要在一个地方做更改即可:
public class Customer
{
? private string _name;
? public string Name
? {
??? get
??? {
????? return _name;
??? }
??? set
??? {
????? if (( value == null ) ||
??????? ( value.Length == 0 ))
??????? throw new ArgumentException( Name cannot be blank,
????????? Name );
????? _name = value;
??? }
? }
? // ……
}
如果使用的是公有数据成员,我们就要寻找并修改所有设置Customer的Name的代码,那将花费大量的时间。
另外,由于属性是采用方法来实现的,因此为它们添加多线程支持就更加容易——直接在get和set方法中提供同步数据访问控制即可:
public string Name
{
? get
? {
??? lock( this )
??? {
????? return _name;
??? }
? }
? set
? {
??? lock( this
文档评论(0)