《Effective C#》 翻译札记
2007-04-24 23:30
169 查看
文章来源:MSDN http://www.microsoft.com/china/msdn/library/langtool/vcsharp/EffectiveCsharp.mspx?mfr=true
发布日期: 6/27/2005 | 更新日期: 6/27/2005作者:李建忠
最 近在翻译Bill Wagner先生的《Effective C#》一书,由于自己早先也有写作Effective .NET的打算,所以对书中很多条款,也有很多自己的思考。如果作为译注来添加,担心把最后的译本添得四不象,不添又甚感遗憾。遂考虑把翻译过程中自己的 所思所想直接记录下来,并在自己的blog(http://blog.dreambrook.com/jzli/)上开辟专门的Effective C#区,供大家讨论打磨,弥补作/译者认识不足的地方,相信也许可以收到正常出版渠道不能取得之效果。言归正传,开始我的Item讨论之旅。
本页内容
使用属性,避免将数据成员直接暴露给外界 | |
明辨值类型和引用类型的使用场合 |
使用属性,避免将数据成员直接暴露给外界
Item:Always Use Properties Instead of Accessible Data Members.学习研究.NET的早期,经常碰到一些学习C#/.NET的朋友问,要属性这种华而不实的东西做什么?后来做项目时也时常接到team里的人的抱怨反馈,为什么不直接放一个public字段?如:
class Card { public string Name; }
而非要做一个private字段+public属性?
class Card { private string name; public string Name { get { return this.name;} set { this.name=value;} } }
我记得在早期的一个项目里,team中的一个朋友甚至厌烦了写private字段+public属性,尤其是碰到一大堆臃肿的data object class的时候,索性自己写了一个小工具,来提供一个类的字段名和类型,然后自动为该类生成相应的private字段+public属性。
我 在编程的时候是个彻底的实用主义者,用稍微高雅一点的话说叫“不喜欢过度的设计”。如果真的像上面那样写Card,而且在将来没有什么改变的需求,我也不 喜欢像上面第2段程序那样把事情故意搞得复杂。但如果从component的角度来讲,总有一些class是要供外部长久地使用,也潜在地在将来有被改变 的需求。这时候,提供属性就很有必要了。
这就是这个Item试图要归纳的使用属性的理由:
1. | 可以对赋值做校验、或者额外的处理 |
2. | 可以做线程同步 |
3. | 可以使用虚属性、或者抽象属性 |
4. | 可以将属性置于interface中 |
5. | 可以提供get-only或者set-only版本,甚至可以给读、写以不同的访问权限(C# 2.0支持) |
但 如果没有上述理由,而且日后对程序做大的改动可能性比较小时,我想也大可不必非要把每个public字段都要变成属性。比如在设计一些轻型的 struct,用于互操作的时候,直接使用public字段没什么不好。所以,感觉本条目Bill Wagner先生使用“Always Use Properties Instead of Accessible Data Members”显得太过强硬。
其实,这里的讨论也表明阅 读《Effective C#》一书时需要注意的地方,即Effective原则并不是放之四海而皆准的。不同的项目(组件化、复用程度较高的项目?还是“一次编写、N年都 run”的项目),不同的角色(类库/组件开发人员?还是应用程序开发人员?),有着不同的Effective准则。事实上,书中很多Items都是从类 库/组件开发人员的角度来考虑的。
关于属性的性能问题需要谈一点,如果仅仅是简单地以存取模式来使用属性,在相当程度上是没有性能损失的。 因为在JIT编译过程中已经做了inline的处理。不过inline处理还是有一些基本的条件,有些情况下JIT编译器不会inline,比如虚调用, 方法的IL代码长度过长(目前CLR的规定是超过32bytes为代码长度过长),有复杂的控制流逻辑,有异常处理等。这些条件都是要么根本不能使用 inline(比如虚属性),要么inline的代价太大,容易导致代码的bloat,要么是inline起来很费时间——已经丧失了inline的意 义,因为.NET的inline机制发生在JIT过程中。使用属性有个别让人感觉不舒服的地方,比如它影响开发人员的开发效率,但对代码运行的效率不产生 影响。
返回页首
明辨值类型和引用类型的使用场合
Item:Distinguish Between Value Types and Reference Types这 个条款讨论的是类型设计时候的tradeoff——是将类型设计为结构还是类。Bill Wagner先生给出了一个原则“值类型用于存储数据,引用类型用于定义行为(value types store values and reference types define behavior)”。
如何判断这个原则的适用性,Bill Wagner也给出了一个方法,那就是首先回答下面几个问题:
1. | 该类型的主要职责是否用于数据存储? |
2. | 该类型的公有接口是否都是一些存取属性? |
3. | 是否确信该类型永远不可能有子类? |
4. | 是否确信该类型永远不可能具有多态行为? |
1. | 由于值类型实例在栈和托管堆之间的转换而导致的box/unbox,以及由此带来的托管堆上的垃圾。 |
2. | 值类型默认情况下采用的是值拷贝语义,如果是比较大的值类型,在传递参数和函数返回值时,同样会带来性能问题。 |
关于第2条,Scott Meyers先生在Effective C++的第22条“尽量使用pass-by-reference(传址),少用pass-by-value(传值)”中讲的比较清楚。虽然由于C#中的结 构类型具有默认的深拷贝语义,没有拷贝构造器的调用。而且结构类型也没有子类,因此在某种程度上来讲不具有多态性,也就没有C++对象传值时可能出现的切 割(slicing)效应。但是值拷贝的成本仍然不小。尤其是在这个值类型比较大的情况下,问题就比较严重。实际上,在.NET框架的Design Guidelines for Class Library Developers文档中,在说明什么时候应该使用结构类型的时候,其中提到了一项原则(还有其他一些并行原则)——类型实例数据的大小要小于16个字 节。该文档主要是从类型的运行效率层面来考虑的,而Bill Wagner先生这里的条款主要是从类型的设计层面来考虑的。
从上述两条讨论 来看,我个人倾向于对结构类型采取更为保守的设计策略。而对于类则可以积极大胆地使用。因为“将结构类型不适当地设计为类”带来的不良后果要远远小于“将 类不适当地设计为结构类型”所带来的不良后果。就目前的经验来看,我甚至认为只有和非托管互操作打交道的情况才是使用结构类型最充足的理由,其他情况都要 “三思而后行”。当然,在C# 2.0中引入泛型技术之后,box/unbox将不再是一个沉重的负担,应付一些非常轻量级的场合,结构类型依然有自己的一席之地。
相关文章推荐
- 《Effective C#》 翻译札记(转载)
- [转贴]《Effective C#》 翻译札记
- 《Effective C#》 翻译札记
- 《Effective C#》 翻译札记
- Effective C# 原则23:避免返回内部类对象的引用(翻译)
- POSA翻译札记--"Critical Section" vs "Critical Region"
- Effective C# 原则5:始终提供ToString()(翻译)
- Effective C# 原则8:确保0对于值类型数据是有效的(翻译)
- 翻译札记
- [K/3Cloud]ksql翻译札记
- [K/3Cloud]ksql翻译札记
- Effective C# 原则3:选择is或者as操作符而不是做强制类型转换(翻译)
- Effective C# 第二章:.Net资源管理(翻译)
- [K/3Cloud]ksql翻译札记
- 翻译工具开发学习札记
- 决定做个Effective C# 第二版翻译
- 札记:翻译-使用Scene和Transition实现【场景切换】动画效果
- java Excel API 简介(翻译)
- Box2D v2.1.0用户手册翻译 - 第03章 公共模块(Common)
- Runtime 官方翻译