《Effective_C__中文版_改善C_程序的50种方法.docx》由会员分享,可在线阅读,更多相关《Effective_C__中文版_改善C_程序的50种方法.docx(189页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、Effective Ct中文版 改善C#程序的50种方法0 , cunEffective 呼 1c、E y Visual Studio Magazine 等技术杂志的专栏作者。他的 blog 是 http:/www. srtsolutions. com/ public/blog/20574I可以通过 wwagnerSR7Solutions. com 与他联系。编辑推荐业界专家经验荟萃,讲述从优秀到卓越的秘诀,涵盖C#2.0。“一直以来,读者们总在不停地问我,什么时候写Effective C#?本书的出版使我如释重负。 令人高兴的是,我本人已经从阅读Bill的著作中获益良多,相信读者也会和我样。
2、”Scott Meyers, Effective C+作者,世界级面向对象技术专家C#与C+、Java等语言的相似性大大降低了学习难度。但是,C#所具有的大量独特的特性和实 现细节。有时又会使程序员适得其反:他们往往根据既有经验,错误地选用了不恰当的技术。从而 导致各种问题。与此同时,随着数年来C#的广泛应用,业界在充分利用C#的强大功能编写快速、高 效和可靠的程序方面也枳累了丰富的最佳实践。本书秉承了 Scott Meyers 的 Effective C+和 Joshua Bloch 的 Effective Java 所开创的伟大 传统.用真实的代码示例,通过清晰、贴近实际和简明的阐述,以条
3、款格式为广大程序员提供凝聚 了业界经验结晶的专家建议。本书中,著名.NET专家BiII Wagner就如何高效地使用C#语言和.NET库。围绕C#语言元素、.NET 资源管理、使用C#表达设计、创建二进制组件和使用框架等重要主题,讲述了如何在不同情况下使 用最佳的语言构造和惯用法,同时避免常见的性能和可靠性问题。其中许多建议读者都可以举一反 三。立即应用到自己的日常编程工作中去。刖S本书就如何高效使用C#语言和.NET库,为程序员们提供了一些实用的建议。本书由50个关键 条款(也可看作是50个小主题)组成,这些主题反映了我(及其他C#顾问)和C#开发人员共事时 遇到的最常见问题。与很多C#开发
4、人员一样,我是在从事10多年C+开发之后开始使用C#的。在本书中,讨论了 哪些情况下遵循C+实践可能会在使用C#时引发的问题。有一些使用C#的开发人员有着深厚的Java 背景,他们可能会发现有些变化相当明显。因为从Java到C#, 些最佳实践发生了改变,我建议 Java开发者要格外注意有关值类型的论述(参见第1章)。此外,.NET垃圾收集器和JVM垃圾收集 器的行为方式也不尽相同(参见第2章)。本书中的条款汇集了我最常提供给开发者的建议。虽然并非所有条款都是通用的,但大多数条 款都可以很容易地应用到日常的编程工作中。这些条款涵盖了对属性(条款1)、条件编译(条款4)、 常量性类型(条款7)、相
5、等判断(条款9)、ICloneable (条款27)和new修饰符(条款29)的 论述。我的经验是,在大多数情况下,减少开发时间和编写出色的代码应该是程序员的主要目标。 某些科学和工程应用程序最重视的可能是系统的整体性能。対其他应用程序而言,凡事都应该围绕 可伸缩性展开。对于不同的目标,可能会找到某些情况下比较重要(或不太重要)的信息。针对这 问题,我设法对各种目标进行了详细的解释说明。书中关于readonly和const (条款2)、可序 列化的类型(条款25)、CLS兼容(条款30)、Web方法(条款34)和DataSet (条款41)的讨论 针对某些特定的设计H标。这些目标在相应的条款中
6、有清楚的说明,这样读者就可以在特定的情况 下决定最适用的做法。虽然本书中的每个条款都是独立的,但是这些条款是围绕一些重要的主题(如C#语法、资源管 理和对象及组件设计)组织起来的,理解这一点非常重要。这并非无心之举。我的目的就是通过将 毎个条款构建在之前的条款之上,并合理地利用之前的条款,来让读者最大限度地学习书中的内容。 尽管如此,大家仍然不要忘了举一反三。对于特定的问题,本书也可以作为个理想的查询工具。要记住的是,本书并不是C#语言的教程或指南,也不是为了教授大家C#语法或结构。我的目 标是为大家在不同的情况下使用什么语言构造最好提供指导。本书面向的读者本书是为专业的开发大员,也就是那些在
7、日常工作中使用C#的程序员们编写的。本书的阅读前 提是读者有面向对象的编程经验,并且至少用过种C系列语言(C、C+、C#或Java)。有Visual Basic 6背景的开发人员在阅读本书之前,应该先熟悉C#语法和面向对象设计。另外,读者应该在.NET的重要领域有一些经验:Web Services, ADO. NET, Web Forms和Windows FormSo为了充分利用本书,大家应该理解.NET环境处理程序集的方式、微软中间语言(MSIL)和可执 行代码。C#编译器生成的程序集会包含MSIL,我经常将其简写为IL。加载程序集的时候,JIT(Just In Time)编译器会将MSIL
8、转换为机器可执行的代码。C#编译器确实会执行些优化,但是JIT编 译器会负责处理很多更高效的优化,如内联。在书中,我对各种优化所涉及的过程进行了说明。这 种两阶段的编译过程对于在不同情形下哪种构造的表现最佳有着很重要的影响。本书内容第1章“C#语言元素”讨论的是C#语法元素和System. Object的核心方法,System. Object是 编写每个类型都要涉及的。声明、语句、算法和System. Object接口,这些都是编写C#代码时必 须时刻记住的主题。此外,与值类型和引用类型之间的区别直接相关的条款也都在本章。根据使用 的是引用类型(类)还是值类型(结构),很多条款内容都有一些不同
9、。在深入阅读本书之前,我 强烈建议大家先阅读有关值类型和引用类型的讨论(条款8)。第2章.NET资源管理”涵盖了 C#和.NET的资源管理问题。大家会学习如何针对.NET管理的 执行环境优化资源分配和使用模式。是的,.NET垃圾收集器使我们的工作简单了很多。内存管理是 环境的职责,而非开发人员的职责。但是,我们的行为对垃圾收集器在应用程序中的执行效果会产 生大的影响。而且,尽管内存不是我们的问题,但管理非内存资源仍然是我们的职责,后者可以通 过【Disposable进行处理。在这里,大家可以学习.NET中资源管理的最佳做法。第3章“使用C#表达设计”从C#的角度讲解了面向对象设计。C#提供了丰
10、富的工具供我们使用。 有时候,相同的问题可以用很多不同的方法解决:使用接口、委托、事件或者特性和反射。选用哪 种方式,对系统将来的可维护性会产生很大的影响。选择最佳的设计表示可以帮助程序员们更容 易地使用类型。最自然的表示会使我们的意图更加淸晰。这样,类型就会比较容易使用,而且不容 易误用。第3章中的条款集中讲解了我们所做的设计决定,以及各种C#惯用法最适用的场合。第4章“创建二进制组件”讲解了组件和语言互操作性。大家将学习如何在不牺牲C#功能的情 况下,编写可被其他.NET语言使用的组件。还将学习如何将类细分成组件,来升级应用程序的某些 部分。我们应该能在不重新发布整个应用程序的情况下发布组
11、件的新版本。第5章“使用框架”讲解了. NET框架未充分使用的部分。我看到很多开发人员非常希望创建自 己的软件,而不是使用已经构建好的软件。这可能是由.NET框架的体积造成的,也可能因为框架是 全新的。这些条款涵盖了框架中那些我曾见过开发人员做重复劳动、而非使用业已存在的功能的部 分。通过学习更高效地使用框架,大家可以节省宝贵的时间。第6章“杂项讨论”以不适合其他分类的条款以及对未来的展望作为全书的结尾。有关C# 2.0、 标准、异常安全(exception-safe)的代码、安全和互操作的信息,都可以在这里找到。关于条款我写这些条款是为了向大家提供编写C#软件的简洁明了的建议。书中有一些指导
12、方针是通用的, 因为它们会影响程序的正确性,如正确初始化数据成员(参见第2章)。有一些指导方针不是很容 易理解,并且在.NET社区中引发过很多争论,如是否使用ADO.NET DataSet。我个人认为使用它们 可以节省很多时间(参见条款41),其他些专业的程序员,同时也是我非常尊敬的程序员,对此 并不同意。它其实取决于我们正在构建的软件性质。我的立场是尽量节省时间。如果是编写大量在 基于.NET和基于Java的系统之间传输信息的软件,DataSet就是个糟糕的主意。在整本书中,我为 所做的全部建议都给出了理山。如果其理由并不适用于你碰到的情况,那就不要采纳书中的建议。 当建议是普遍适用时,我通
13、常会省略其显而易见的理由:如果不这样做,程序就起不了作用。版式和代码约定写编程语言图书的个困难之处在于,语言设计者用些英文单词表示非常特殊的新含义,这 就导致了一些很难理解的句子。Develop interfaces with interfacesM就是个例子。因此, 我在使用语言关键字时,都采用了代码体。本书使用了很多相关的C#术语。在提到类型的成员时,它是指可以成为类型的一部分的任何定 义:方法、属性、字段、索引器、事件、枚举或者委托。当应用的只是一种定义时,我会使用一个 更加具体的术语。对于书中的许多术语,大家可能熟悉,也可能还不熟悉。当这些术语第一次在正 文中出现时,它们会以楷体形式表
14、现,并给出定义。本书的范例都是简短、专注的代码段,以示范特定条款中的建议。列出它们是为了强调遵循建 议的好处。它们并不是可以加入到读者当前程序中的完整范例。大家不能简单地复制代码清单,然 后编译它们。所有代码淸单我都省略了很多细节。在所有情况下,我们都预设已存在如下常用的using 语句:using System;using System. 10;using System. Collections;using System. Data;当使用不太常见的命名空间时,我会确保让读者看到相关的命名空间。简短的范例会使用完全 限定类名称,而长的范例则会包含不太常用的using语句。范例中的代码也比较随
15、意。例如,当显示下列代码时:string si = GetMessage();如果和论述的内容无关,我可能不会显示GetMessage ()例程的主体。当我省略代码时,读者可 以假定缺失的方法做的是一些明显且合理的事情。我这样做的目的是为了让我们把焦点聚在特殊的 主题上。通过省略和主题无关的代码,我们的注意力就不会分散。这样还能让各个条款保持简短, 以使大家能够在短时间内完成学习。关于C# 2.0我之所以对新的C# 2.0版本所言甚少,有两个原因。首先,本书中的大部分建议也同样适用于 ctt 2.0o虽然C# 2.0是个非常重大的升级版本,但它是建立在C# 1.0基础之上的,且并没有让 现如今
16、的建议失效。对于最佳实践有可能发生变化的地方,我已经在文中给出了说明。第二个原因是现在编写新的C# 2. 0功能的高效用法还为时过早。本书的内容是基于我以及我的 同事使用C# 1. 0的已有经验。我们对于C#2. 0中的新功能还没有足够的经验,因而也就不了解能够 应用到I常任务中的最佳做法。当在书中编写C# 2.0新功能的高效用法的时机还未成熟时,我并不 想误导读者。建议、反馈及获取本书的更新内容本书内容基于我的经验以及和同事们的交流。如果读者有不同的经验,或者有任何疑问或意见, 我愿洗耳恭听。请通过电子邮件和我联系:wwagner。我会把这些意见放在网上, 作为本书的延伸。登录www. sr
17、tsolutions. com/EffectiveCSharp,可以看到当前的讨论。致谢虽然写作似乎是一件孤独的事情,但本书却是一大群人的成果。我非常幸运,认识两位出色的 编辑 Stephane Nakib 和 Joan Murray Stephane Nakib 第一次联系我为 Addison Wesley 写作是在 一年多以前。我当时心存疑虑,因为书店里到处都是.NET和C#方面的书籍。在C# 2.0面世了足够 的时间,可以大书特书之前,我一直看不出为C#和.NET再写一本参考书、教程或编程书籍的必要性。 我们讨论过好几个想法,话题总是回到写本有关C#最佳实践的图书。在进行这些讨论的过程中
18、, Stephane告诉我,Scott Meyers开始着手主编个Effective系列,其风格延续了他的Effective C+系列图书。我买的Scott的三本书都被我翻得非常破旧。我还将它们推荐给了我认识的每一位专 业C+程序员。他的写作风格淸晰而简明。每个建议条款都有过硬的理由。Effective从书是很好 的资源,而且其体例使得读者很容易就能记住其中的建议。我认识很多C+开发人员,他们复印了 书的目录,并把它钉在书房的墙,不断提醒自己。Stephane-提到写作Effective C#的想法,我 就欣然接受了这个机会。这本书把我曾为C#开发人员给出的所有建议收录在起。我很荣幸能成为 该
19、系列图书的个作者。和Scott 一起工作让我学到了很多东西。我真心希望本书能够像Scott的 书提高了我的C+技巧那样,帮助大家提高C#应用技巧。Stephane帮助落实了写作Effective C#的想法,她审读了提纲和草稿,并在该书的早期写作过 程中给予了充分的支持。当她抽身离开的时候,Joan Murray接管了这个项目,并毫无倦怠地负责 了原稿的写作管理。Ebony Haight作为编辑助理,在整个过程中提供了不间断的帮助。Krista Hansing 完成了所有编辑和转换编程行话的工作。Christy Hackerd完成了所有把Word文档转变为成书的 作。书中如有错误,应由我来负责
20、。出色的审稿团队修改了绝大多数的错误、冗长和表述不清的问 题。最值得一提的是,Brian Noyes、Rob Steel、Josh Holmes和Doug Holland使得最终的正文比 初期的草稿更加正确和有用。另外,还要感谢安阿伯计算机学会、大湖区.NET用户组、Greater Lansing用户组和西密歇根州.NET用户组的所有成员,他们听取了有关这些条款的议论,并提供了 出色的反馈。特别要提到的是,Scott Meyers的参与对本书的最终版本有着巨大的积极影响。和他讨论本书 的早期草稿,使得我更加明白为什么自己会把Effective C+丛书用得破旧不堪。再小的问题,也 逃不过他的眼
21、睛。我要感谢 MyST Technology Partners (myst-technology. com)的 Andy Seidl 和 Bill French 我使用了一个基于MyST的安全博客网址向审稿人公布了各个条款的早期草稿。之后我们向公众公开 了部分站点,以便大家能够以在线的格式看到本书的部分内容。登录 www. srtsolutions. com/Effect iveCSharp,可以阅读在线版本。到目前为止,我已经为杂志写了好几年的文章,我要在此感谢那个将我引进门的大:Richard Hale Shawo他在自己参与创办的杂志Visual C+开发者上邀请我这个未经检验的作者开设
22、了一 个专栏。如果没有他的帮助,我不会发现自己对写作的热爱。没有他最初给我的帮助,我也不会有 机会为 Visual Studio 杂志、C# Pro 或 ASP. NET Pro 撰稿。一路走来,我幸运地和不同杂志的很多出色的编辑共过事。我想把他们的名字全都列在这里, 但空间不允许。有一个大非提不可,那就是Elden Nelson。我享受与他共事的所有时光,他对我的 写作风格产生了很大的积极影响。我的业务伙伴Josh Holmes和Dianne Marsh,他们容忍了我对公司业务的有限参与,而让我把 时间用来写作本书。他们还帮助审阅了我的原稿、想法和条款中的思想。在整个漫长的写作过程中,我的父
23、母Bill和Alice Wagner 做事要有始有终”的忠告,成为 我最终完成本书的唯一原因。最后也是最重要的,我要感谢我的家人Marlene、Lara、Sarah和Scott。写书会占用大量的业 余时间。在我为本书付出了所有时间之后,他们表现出来的却是始终如一的耐心。第一章C#语言元素为什么程序已经可以正常工作了,我们还要改变它们呢?答案就是我们可以让它们变得更好。 我们常常会改变所使用的工具或者语言,因为新的工具或者语言更富生产。如果固守旧有的习惯, 我们将得不到期望的结果。对于C#这种和我们已经熟悉的语言(如C+或Java)有诸多共通之处的 新语言,情况更是如此。人们很容易回到旧的习惯中
24、去。当然,这些旧的习惯绝大多数都很好,C# 语言的设计者们也确实希望我们能够利用这些旧习惯下所获取的知识。但是,为了让C#和公共语言 运行库(Common Language Runtime, CLR)能够更好地集成在起,从而为面向组件的软件开发提 供更好的支持,这些设计者们不可避免地需要添加或者改变某些元素。本章将讨论那些在C#中应该 改变的旧习惯,以及对应的新的推荐做法。原则1:始终能的使用属性(property),而不是可直接访问的Data MemberAlways use properties instead of accessible data members.在C#里,Propert
25、y已经晋升为类公民。如果你的类里还有Public的变量,Stop!如果你还 在手写get and set方法,Stop! Property在不破坏你类的封装的情况下,仍可以把类的data member 变成public interface的一部分。访问Property的方式和访问data member的方式样,但Property 是用methods实现的。有些类的成员只能用data最好的表示,比如:你一个客户的名字,个点的坐标,等等。而 Property就是用来欺骗使用你类的客户,让它们错误的认为它们在访问你类的public变量。你还 可以通过Property的实现方法来控制Property的
26、访问。.Net Framework假定你使用Property来让外界访问你类里想让外界访问到的data member (也 就是public data member) 实际上也是这样的,因为.Net的data binding只支持Property,而 不支持public data member的访问。Data binding的目的就是把个object的Property绑定到 一1个用户界面的 control 上,web control 或者 windows form control. Data binding 是通过 reflection来实现的,如下例:textBoxCity. DataBi
27、ndings. Add(*Text*, address, City);这段 code 就是把 textBoxCity 的 Text Property 绑定到 address 这个 object 的 City Property ho如果你把address的City Property改成public data member,这段code是不会运行的。因 为.Net Framework Class Library的设计者不支持你的这种行为,他们认为public data member 是非常不好的行为和习惯,所以他们不会支持,他们想让你遵从正确的Object Oriented设计方法。 Data b
28、inding也不会去找get and set methods,所以一定要用Property,而不是传统的get and set methods.你也许要说,data binding只适用于那些含有要显示在用户界面的元素的类。但实际情况并不 是这样,对于你所有的类,都要使用Property而不是public data member。因为当有新的需求时, 通过修改Property的实现方法来适应这个新的需求,要比在你的程序里修改所有的public data member去适应这个需求容易太多了。比如说你以前定义了 个类customer,现在你发现由于当初的 粗心没有强制customer姓名不能为空
29、,如果你使用了 Property,你可以非常轻松的添加一个检查 机制,如下面这段code:public class Customerprivate string name;getreturn _name; set (if (value = nul1) I I (value. Length = 0) (throw new ArgumentException(*Name can not be blank*, Name); )_name = value; ) /.I如果你使用了 public data member,你就要找遍你的程序,在每个地方都修改,那样就很愚蠢 了。而且浪费了无数青春好时光。因
30、为Property是用methods实现的,所以添加multi-threaded的支持是非常方便的。比如想 要添加同步访问的支持:public string Name ( get (lock(this) return _name;) )set (lock(this)name = value; )因为Property是用methods实现的,所以它拥有methods所拥有的一切。Property可以被定义为virtual: public class Customer (private string name;public virtual string Namereturn name;set(_n
31、ame = value;)/.显而易见,你也可以把Property扩展为abstract,甚至成为interface的部分。 public interface INameValuePair(object Name(get;)object Value(get;set;J你当然也可以扩展出const和nonconst版本的interface。public interface IConstNameValuePair(object Name(get;)object Value(get;)public interface INameValuePair(object Value (get;set;)/us
32、age:public class Stuff : IConstNameValuePair, INameValuePairprivate string name;private object _value;#region 1ConstNameVa1uePair Members public object Name (get return _name; )object IConstNameValuePair. Value (get (return _value;) )ttendregion#region INameValuePair Members public object Value (get
33、 ( return value; set (value = value;)#endregion)如前所述,Property是访问内部数据的method的扩展,它拥有member function的切特性。因为实现Property访问的方法get and set是独立的两个method,在C# 2.0中,你可以给它 们定义不同的访问级别,来更好的控制类成员的可见性,如下例:public class Customer private string name; public virtual string Name (get return _name;protected setname = value
34、;IProperty的语法已经超越了单纯的data field。如果你的类包含indexed item,你可以使用 indexer(参数化的Property)你可以创建一个可返回一个序列元素的Property如下例: public int thisint index (get (return _theValuesindex;) set (theValuesindex = value;) /usage: int val = MyObjecti;indexer和单元素Propert/有着相同的特性。一维的indexer可以用于data binding,:维和多维的indexer 可以用来实现其他的
35、数据结构,比如map和dictionary:public Address thisstring name ( get (return theValuesname; )theValues name = value;)I j多维的indexer的每个axis上的数据类型可以相同,也可以不同:public int thisint x, int y(get(return ComputeValue(x, y);)public int thisint x, string namereturn CoraputeValue(x, name);所有的indexer都必须也只能用this来定义,所以参数表相同的i
36、ndexer,每个类最多只能有一个。因为使用Property和data member对于数据访问的code没有什么区别,比如:public class Customer(public string Name; /.在这个类中使用了 public data member,数据访问的code如下:string name = CustomerOne. Name;CustomerOne. Name = customer name*;你也许会想,如果在以后的修改中,用Property來代替public data member是可行的,因为数据访问的 code相同,但实际上这是行不通的。确实,访问Prop
37、erty和访问data member的code是相同的,但Property 不是data,访问Property所产生的!Lcode和数据访问的ILcode是不样的。所以访问Property和访问data member只具有code兼容性,而不具有binary的兼容性。如果有兴趣,你可以使用Reflector (http: Property 和 public data member 的类。你会发现在使用Property的类中,存在.property directive这个directive定义了 Property的类型以及 get and set实现方法 Get and set都被标注为hide
38、bysig, specialname。也就是说它们不能被C#源代码直接 调用,它们也不是正是的类型定义。你只能通过Property来访问它们。C#的编译器会根据类的情况(是用Property还是data member)来自动产生不同的IL code。如上所述,访 问Property和访问data member只具有code兼容性,而不具有binary的兼容性。所以,如果你改变最初的设 计,用Property來代替public data member的话,你必须重新编译整个程序。这使得升级已经部署的程序或 assembly是非常的麻烦。那么两种实现谁的效率更好呢? Property确实不会比pu
39、blic data member快,但也不一定会慢。因为JIT 对Property的存取方法set and get进行inline的优化。这时,Property和public data member的效率是 样的。即使Property的存取方法没有被inline优化,它和public data member的效率差别也只是个可以忽略 的function caH。只有在很少的情况下,这种差别才可以被测量出来。总而言之,当你想让你类内部的数据被外界访问到时(不管是public还是protected), 定要用Property . 对于序列和字典,使用indexer。你类的data member永远
40、应该是private,绝无例外。使用Property,你可以 得到如好处:1 . Data binding 支持2 .对于需求变化有更强的适应性,更方便的修改实现方法记住,现在多花1分钟使用Property,会在你修改程序以适应设计变化时,为你节约n小时。原则2:为你的常量选择readonly而不是constPrefer readonly to const对于常量,C#里有两个不同的版本:运行时常量和编译时常量。因为他们有不同的表现行为,所以当你使用不当时,将会损伤程序性能或者出现错误。两害相权取其轻,当我们不得不选择个的时候,我们宁可选择一个运行慢一点但正确的那一个,而不是运行 快一点但有错
41、误的那个。其于这个理由,你应该选择运行时常量而不是编译时常量(译注:这里隐藏的说明了编译时 常量效率更高,但可能会有错误)。编译时常量更快更直接,但在可维护性上远不及运行时常量。保留编译时常量是为了满足那些对性能要求克刻, 且随着程序运行时间的过去,其值永远不发生改变的常量使用的(译注:这说明编译时常量是可以不被C#采用的, 但考虑到性能问题,还是做了保留)。你可以用关键字readonly来声明(declare) 个运行时常量,编译时常量是用关键字const声明的。/Compile time constant:public cocnst int -Millennium = 2000;/Runtime constant:public static readonly int _ThisYear = 2004;编译时常量与运行时常量不同之处表现在如何对他们的访问上。个编译时常量会被目标代码屮的值宜接取代。下面的代码:if(myDateTime.Year = = _M 川 ennium)会与面写的代码编译成完全相同的IL代码:if(myDateTime.Year = 2000)运行时常量的值是在运行时确定的。当你引用个只读常量时(read-o