使用属性避免将数据成员直接暴露给外界
学习研究.NET早期经常碰到些学习C#/.NET朋友问要属性这种华而不实东西做什么?后来做项目时也时常接到team里人抱怨反馈为什么不直接放个public字段?如:
Card
{
public Name;
}
而非要做个private字段+public属性?
Card
{
private name;
public Name
{
get { this.name;}
{ this.name=value;}
}
}
我记得在早期个项目里team中个朋友甚至厌烦了写private字段+public属性尤其是碰到大堆臃肿data object 时候索性自己写了个小工具来提供个类字段名和类型然后自动为该类生成相应private字段+public属性
我在编程时候是个彻底实用主义者用稍微高雅点话说叫“不喜欢过度设计”如果真像上面那样写Card而且在将来没有什么改变需求我也不喜欢像上面第2段那样把事情故意搞得复杂但如果从component角度来讲总有些是要供外部长久地使用也潜在地在将来有被改变需求这时候提供属性就很有必要了
这就是这个Item试图要归纳使用属性理由:
1.可以对赋值做校验、或者额外处理
2.可以做线程同步
3.可以使用虚属性、或者抽象属性
4.可以将属性置于erface中
5.可以提供get-only或者-only版本甚至可以给读、写以区别访问权限(C# 2.0支持)
个人感觉3、4条是属性最大优点可以填补没有“虚字段”或“抽象字段”缺憾在设计组件时候非常有用也体现了C#这样component-oriented语言精神内涵
但如果没有上述理由而且日后对做大改动可能性比较小时我想也大可不必非要把每个public字段都要变成属性比如在设计些轻型struct用于互操作时候直接使用public字段没什么不好所以感觉本条目Bill Wagner先生使用“Always Use Properties Instead of Accessible Data Members”显得太过强硬
其实这里讨论也表明阅读Effective C#书时需要注意地方即Effective原则并不是放的 4海而皆准区别项目(组件化、复用程度较高项目?还是“次编写、N年都run”项目)区别角色(类库/组件开发人员?还是应用开发人员?)有着区别Effective准则事实上书中很多Items都是从类库/组件开发人员角度来考虑
有关属性性能问题需要谈点如果仅仅是简单地以存取模式来使用属性在相当程度上是没有性能损失在JIT编译过程中已经做了inline处理不过inline处理还是有些基本条件有些情况下JIT编译器不会inline比如虚思路方法IL代码长度过长(目前CLR规定是超过32s为代码长度过长)有复杂控制流逻辑有异常处理等这些条件都是要么根本不能使用inline(比如虚属性)要么inline代价太大容易导致代码bloat要么是inline起来很费时间——已经丧失了inline意义.NETinline机制发生在JIT过程中使用属性有个别让人感觉不舒服地方比如它影响开发人员开发效率但对代码运行效率不产生影响
明辨值类型和引用类型使用场合
这个条款讨论是类型设计时候tradeoff——是将类型设计为结构还是类Bill Wagner先生给出了个原则“值类型用于存储数据引用类型用于定义行为(value types store values and reference types behavior)”
如何判断这个原则适用性Bill Wagner也给出了个思路方法那就是首先回答下面几个问题:
1.该类型主要职责是否用于数据存储?
2.该类型公有接口是否都是些存取属性?
3.是否确信该类型永远不可能有子类?
4.是否确信该类型永远不可能具有多态行为?
如果所有问题答案都是yes那么就应该采用值类型这样判断确实有很好理由支撑但是我个人认为“将这4个问题回答为yes”还不足以构成采用值类型全部理由在很多项目实战中我发现值类型带来性能问题不可小视值类型带来性能问题主要有两个:
1.由于值类型例子在栈和托管堆的间转换而导致box/unbox以及由此带来托管堆上垃圾
2.值类型默认情况下采用是值拷贝语义如果是比较大值类型在传递参数和返回值时同样会带来性能问题
有关第1条Bill Wagner在本条款中提到了“引用类型会给垃圾收集器带来负担”这个表面看似正确判断但是由于box/unbox效应有些情况下反倒是值类型给垃圾收集器带来了更多负担比如将些值类型放到个集合中然后又频繁地对其进行读写操作如果碰到这种情况我想“放弃结构而采用类”未尝不是种更好做法事实上将个用作数据存储值类型(比如.Drawing.Po)添加到个集合(.Collections.ArrayList)中是个太常见不过操作不过C# 2.0中新引入泛型技术对box/unbox问题有极大改善
有关第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先生这里条款主要是从类型设计层面来考虑
从上述两条讨论来看我个人倾向于对结构类型采取更为保守设计策略而对于类则可以积极大胆地使用“将结构类型不适当地设计为类”带来不良后果要远远小于“将类不适当地设计为结构类型”所带来不良后果就目前经验来看我甚至认为只有和非托管互操作打交道情况才是使用结构类型最充足理由其他情况都要“ 3思而后行”当然在C# 2.0中引入泛型技术的后box/unbox将不再是个沉重负担应付些非常轻量级场合结构类型依然有自己席的地