《Effective C#》--Bill Wagner


《Effective C#》-- Bill Wagner.
 
Chapter 1  C# Language Elements
    该章主要讲一些基本的C#语言性质,强调了需要改正的编程习惯,从C/C++时代积累的习惯。
Item 1:   Always Use Properties Instead of Accessible Data Members.

    总是使用Properties代替直接访问数据成员。

    以前曾经有个误解,认为使用get/set会生成对应的方法,这样会降低效率。实际上,JIT编译器会采用inline方式处理这些方法调用,而内联方法和直接访问数据成员的效率差别是微乎其微的。但是,要注意get/set中如果有try/catch将会阻止编译器生成内联方法。

    众所周知的,目前C#1.1中get/set的访问修饰符必须一致,这有时候不能满足设计要求,但幸运的是,在C#2.0中可以get/set可以使用不同的访问修饰符。

    索引器只能使用this[]命名,不能重命名,但我实在想不出理由要去重命名indexer.

Item 2:   Prefer readonly to const.

    尽量使用readonly代替const。

    readonly和const的差别:

    1)    readonly - runtime constants.

           const - complie-time constants.

    2)    const只能是数值和字符串,只能通过=初始化。

           readonly可以是实例常量,可以通过new构造。

    3)    readonly常量是运行期兼容的,一个程序集里面的readonly常量的变更,不需要重新编译引用该常量的其他程序集。

           const常量在编译期被赋值,所以一个const常量的变更,将无法使其他引用该常量的其他程序集同步,而必须重新编译。

    4)    const常量比readonly常量效率稍微高一点,比较适合于自然界恒定量,永远不可能会被改变的量,否则宁愿使用readonly。

Item 3:   Prefer the is or as Operators to Casts.

    尽量使用is、as操作符代替转换操作(C风格的转换操作)。

    程序中应该尽量避免强制类型转换。虽然有时候运行时类型检查不是轻易可以避免的,但是使用is、as的原因很简单,因为它们是安全而优雅的。

Item 4:   Use Conditional Attributes Instead of #if.

    使用Conditional Attributes代替#if。
    又需跟旧风格说byebye了,在C的时代,这些预编译指令是如此强大而有诱惑力,以致在目前我的C#程序中还能看到它的痕迹。
    C#的语言特性:条件属性提供了更优雅的方式来实现更多的类似功能。#pragma是该成为历史了。

 

Item 5:   Always Provide ToString().

    总是提供ToString()。

    我们在实现类设计时经常会忘了这个小东西,因此它也喜欢在不如意的时候给我们制造些小麻烦,尤其是在想问它你是谁时它会告诉你啼笑皆非的类名。

 

Item 6:   Distinguish Between Value Types and Reference Type.

    值类型与引用类型间的差别。
    如果不能理解值类型与引用类型间的差别,以及各语言的处理差异,那么就是压根没真正学会现代计算机语言,尤其是在跟数组、方法参数搭边的地方。这在后面的平台间调用还会有相应内容。

 

Item 7:   Prefer Immutable Atomic Value Types.

    尽可能的使用不可变的原语值类型。
    就借用一下java中的词汇,原语类型天生就是线程安全的。有过多线程编程经验的人会深切感受到这有多重要。
    数组本身是引用类型,所以提供一个struct代替array来保存不可变的原语值类型是有必要的。

 

Item 8:   Ensure That 0 Is a Valild State for Value Types.

    确定0是值类型的可用状态。

    中间举例了一个显式赋值的enum作为反例,已经吃过亏了,这实在不是个好主意,在.Net Framework 1.0像1.1过渡时,差点没郁闷死,在以前的文章中已有该记录
    CLR初始化所有的实例值都为0,别忽视了该问题,0对你的类型来说是可用值吗?

 

Item 9:   Understand the Relationships Among ReferenceEquals(), static Equals(), instance Equals(), and operator==.

    理解ReferenceEquals()、静态Equals()、实例Equals()、operator==之间的关系。

    Equality is reflexive, symmetric, and transitive.

    A) public static bool ReferenceEquals(object left, object right);

    B) public static bool Equals(object left, object right);

    C) public virtual bool Equals(object right);

    D) public static bool operator==(MyClass left, MyClass right);

    Conclusion:

    1)不需要重些static Object.ReferenceEquals()和static Equals()。

    2)总是为值类型重写instance Equals()和operator==()。

    3)为引用类型重些instance Equals()。

Item 10: Understand the Pitfalls of GetHashCode().

    理解GetHashCode的缺陷。

    用于Hastable和Dictionary的类型需要生成一个哈希值作为key。而这应该尽量避免,实在避免不了,应该遵循3个规则:
    A) 如果两个对象相等,那么必须生成相同的哈希值;
    B) 任何对象A,A.GetHashCode()在生存期间哈希值不变;
    C) 哈希函数必须根据不同输入生成随机分布的整形值。

 

Item 11: Prefer foreach Loops.

    尽可能使用foreach循环。

    foreach有边界检查,并且该检查是高效的,经过测试,在CLR1.1中,foreach略微比for循环低一点点,但几乎无影响。但却简洁很多,优雅很多,我喜欢。

 Chapter 2 .NET Resource Management

    该章主要讲.NET托管环境的资源处理方式,特别是垃圾收集器的行为机制,托管和非托管资源。
    在之前我已经整理过相关资料,《dotNet垃圾收集使用规则》,源自《.NET框架程序设计》的读书笔记。在学习过CLR的对象模型之后对这些应该非常好理解的。

 

Item 12: Prefer Variable Initializers to Assignment Statements.

    尽量使用变量初始化代替赋值语句
    这还是要区别原语类型和引用类型,还有类成员变量和局部变量,因为CLR在类初始化时会忠实的为成员变量加上构造语句
    我的另外一个观念就是:仅仅在需要的时候才定义变量,而不是像C/C++时代习惯将变量声明在开始处。

 

Item 13: Initialize Static Class Members with Static Constructors.

    使用静态构造块初始化静态类成员

 

Item 14: Utilize Constructor Chaining.

    利用构造链。
    这个从代码风格和效率来讲都是显而易见的。

 

Item 15: Utilize using and try/finally for Resource Cleanup.

    利用using和try/finally进行资源清理。
    using关键字和非托管资源的Dispose模式,优点在Item 18会详细讲到,《dotNet垃圾收集规则》也作了说明。

 

Item 16: Minimize Garbage.

    垃圾最少化。
    介绍了怎样的代码是高效的,并且产生的垃圾资源最少,尽可能的减少垃圾收集的次数,以及避免不必要的手动唤醒垃圾收集器。

 

Item 17: Minimize Boxing and Unboxing.

    装箱、拆箱最少化。
    原语类型与引用类型间的相互转换,确实是低效的而应该尽量避免的。

 

Item 18: Implement the Standard Dispose Pattern.

    实现标准的Dispose模式。
    针对托管非托管资源的销毁方式,给出了一个标准化的处理方式。

 

Chapter 3 Expressing Designs with C#

    列举了针对C#的设计实现方法需要注意的几个基本规则。

 

Item 19: Prefer Defining and Implemeting Interfaces to Inheritance.

    尽可能的采用定义、实现接口的方式代替继承。
    针对接口而不是实现编程,这是基本的OO原则,继承的利弊还是比较明显的。

 

Item 20: Distinguish Between Implementing Interfaces and Overriding Virtual Functions.

    实现接口和覆盖虚函数的差别。
    刚看到该规则时,真没想倒接口实现和override虚函数之间的差别。
    我的通常做法是:定义一个接口,然后定义一个抽象类来实现该接口,提供实现该接口的一些缺省的指南性的虚方法。

 

Item 21: Express Callbacks with Delegates.

    用委托表达回调。
    解藕的一个常用手法,没啥特别的,因为说为什么不采用delegate呢?。

 

Item 22: Define Outgoing Interfaces with Events.

    为事件定义对外接口。
    与客户的交互方式,跟delegate是紧密关联的。

 

Item 23: Avoid Returning references to Internal Class Objects.

    避免返回内部类对象的引用。
    限制外部程序对对象数据的访问是出于自我保护的需要,这是一个常识,别给恶意程序可趁之机。

 

Item 24: Prefer Declarative to Imperative Programming.

    尽量使用声明代替强制编程?。
    跟后面的一个规则很相似,尽量采用Attribute来声明方法的行为,提供更好的可读性和更清晰的代码。

 

Item 25: Prefer Serializable Types.

    尽可能采用序列化类型。
    序列化可以增强一个对象的生命力,尤其对于需要保存恢复或者跨进程传输的对象更是必须如此。

 

Item 26: Implement Ordering relations with IComparable and IComparer.

    通过IComparable和IComparer实现分类排序。
    对于需要进行比较的对象实现这两个接口是必要的。

 

Item 27: Avoid ICloneable.

    避免实现ICloneable。
    深拷贝、浅拷贝的不一致性,对象拷贝的缺省行为决定了Clone方法的岐意性。

 

Item 28: Avoid Conversion Operators.

    避免使用转换操作。
    转换会带来意想不到的对象行为差异,而这些差异是引发故障的根源。

 

Item 29: Use the new Modifer Only When Base Class Updates Mandate It.

    仅仅在基类修改指定要求的情况下使用new修饰符。
    因为这将引起基类方法和继承类方法的不一致性,而这种不一致性是隐藏的。

 

Chapter 4 Creating Binary Components

    创建程序集的一些基本规则。

 

Item 30: Prefer CLS-Compliant Assemblies.

    尽可能使程序集符合CLS。
    这可以让程序集被其他开发者放心使用。类型、语义一致,这是COM时代梦寐以求的。

 

Item 31: Prefer Small, Simple Functions.

    尽可能小而简单的功能。

 

Item 32: Prefer Smaller, Cohesive Assemblies.

    尽可能小而内联性强的程序集。

 

Item 33: Limit Visibility of Your Types.

    限制你的类型可见度。
    这跟上面的Item 23有一点点的联系,同样是出于自我保护避免破坏的原因。

 

Item 34: Create Large-Grain Web APIs.

    创建大颗粒的Web API。
    这是因为Web应用的交互特性决定的,这样可以最大限度的避免来回传输数据的次数,解决远程应用的主要矛盾。

 

Chapter 5 Working with the Framework

    该章强调使用已有的框架资源来进行开发,因为这些资源是经过证明高效而可靠的。不重复发明轮子,这是一个聪明程序员应该牢记的。

 

Item 35: Prefer Overrides to Event Handlers.

    尽可能用Override代替事件句柄。
    当然这是有前提的。又涉及到继承和聚合的取舍,尽量采用聚合,但是Override在继承的情况下优先于定义事件句柄。

 

Item 36: Leverage .NET Runtime Diagnostics.

    有效利用.NET运行时诊断。
    TRACE和DEBUG的应用,有了这些干吗还要费劲自己写诊断方法?

 

Item 37: Use the Standard Configuration Mechanism.

    使用标准化的参数配置机制。
    在经历了INI、Registery之后,已经有了更适宜的配置文件方式。但需要注意的是程序应该对用户安全级别要求尽可能的低,避免不必要的安全请求。

 

Item 38: Utilize and Support Data Binding.

    利用和支持数据绑定。
    第一个规则已经阐述了一些原因,这么浅显的规则为什么一再强调还是有人在违反呢?

 

Item 39: Use .NET Validation.

    使用.NET校验。
    .NET的校验机制比较灵活而高效,正则表达式的正确使用可以极大提高效率。正则表达式不懂?那做啥程序员呢,先去学习再说。

 

Item 40: Match Your Collection to Your Needs.

    根据你的需要选择最适合的集合。
    数组、链表、队列、堆栈,等等,如此众多的集合对象该如何取舍呢?
    这些集合的使用应该首先考虑使用目的,主要用于查询?插入、删除多不多?规模多大?

 

Item 41: Prefer DataSets to Custom Structures.

    尽可能使用DataSet而不是自定义结构。
    这个规则感觉应该斟酌。确实,DataSet基本可以满足开发要求,但用起来却总感觉有点别扭,虽然自定义结构没有DataSet那么强大,但是实际应用往往也不需要使用到那些功能,以我们目前的开发要求来说。售票系统,要求的就是快速、灵活、分布式,那我宁愿使用自定义集合而不是DataSet,DataSet适用于基于Schema的关系型数据,而我的应用侧重于实体对象,关注性能。
    想起theserverside的一篇文章,但是以后的应用中我还是需要认真考虑使用自定义结构的必要性。目前自动生成代码的工具也降低了自定义结构的复杂性。虽然在自动生成代码方面的争论还是激烈的,但个人认为关键还在于使用的场合时机是否适当。
    参考:Brain Noyes -《Picking a Smart Client Communications Technology》http://www.theserverside.net/articles/showarticle.tss?id=PickingSmartClient

 

Item 42: Utilize Attributes to Simplify Reflection.

    利用Attribute代替简单的反射。
    跟Item 24的思想类似,基于声明的编程。

 

Item 43: Don't Overuse Reflection.

    不要滥用反射。
    一般情况下,使用class factories, delegates, interfaces可以达到同样的目的,并且更易于理解和维护。
    关于反射的使用在UnitTestHelper中已经得到充分锻炼。

 

Item 44: Create Complete Application-Specific Exception Classes.

    创建完整的应用程序特定的异常类。
    感觉就是为了便于区别异常,不要throw new Exception,尽可能使用准确的异常类。

 

Chapter 6 Miscellaneous

    杂项,讨论了代码访问安全,托管边界,以及C#的有关资源,发展方向。

 

Item 45: Prefer the Strong Exception Guarantee.

    尽可能使用强异常保证。

    曾经写了关于异常处理的一篇文章放在公司内部的开发项目组网站上,可搬了个地方,找起来不方便了。从本人观点是强烈反对将异常作为控制流的一种方式的。同时只处理必须处理的异常也是我的一个原则。或许这有需要再思考改进的地方。

Item 46: Minimize Interop.

    Interop最少化
    微软提供了一种不太优雅的方式来处理向后兼容,该方式只能说聊胜于无。提供了三种方式以供选择:
    A) Interop
    B) P/Invoke
    C) Managed C/C++
    各有利弊,需要根据场合来选择。但无疑使用Interop是最痛苦的。适当考虑重写遗留代码是值得的。中间还提到Blittable(弱)类型的必要性。确实,原语类型是首选。

 

Item 47: Prefer Safe Code.

    尽可能使用安全代码。
    unsafe code只有在强烈要求的情况下才应该被使用,并且应该和safe code隔离开来。并考虑.NET的安全模型。

 

Item 48: Learn About Tools and Resources.

    相关的工具和资源。

 

Item 49: Prepare for C# 2.0

    为C# 2.0准备好。
    C# 2.0的九大特性讲了四个主要的:generics, iterators, anonymous methods, partial types。所知的还有:Nullable Type, static classes, property accessors, enternal aliases, fixed size buffers.

 

Item 50: Learn About the ECMA Standard.

    学习相关的ECMA标准。
    ECMA:欧洲计算机制造商协会
    ECMA-334: C# 1.0语言标准
    ECMA-335: CLR 1.0标准
    要成就资深的C#程序员,必须花费更多的精力,学习rotor将会是今年上半年必须完成的任务。

 

 

posted @ 2006-04-10 15:56  bengxia  阅读(597)  评论(4编辑  收藏  举报
无觅相关文章插件,快速提升流量