编写高质量C#代码必备技巧(下)

以下为《编写高质量代码:改善C#程序的157个建议》作者【陆敏技】的读书总结,添加了笔者自己的理解或示例。

90:不要为抽象类提供公开的构造方法。abstract class MyAbstractClass{ protected MyAbstractClass(){}}
91:可见字段应该重构为属性。public string Name { get; set; }。不过笔者认为在快速迭代和大量使用属性的情况下反而会降低工作效率。相比属性来讲,给成员起一个好名字更为重要
92:谨慎将数组或集合作为属性。如果是只读属性,会让调用者产生歧义,因为虽然是只读,但是如果集合中的元素为引用类型,这个元素的内部是可以更改的,不可更改的仅仅是集合持有对应元素的引用。
93:构造方法应初始化主要属性和字段。对初始化统一管理,避免遗忘,而且可以方便的看到初始化的数据是否过于庞大。
94:区别对待override和new,前者是覆盖,后者是并存。
95:避免在构造方法中调用虚成员。从而避免因override的存在而造成在父类初始化时调用子类的重写函数。
96:成员应优先考虑公开基类型或接口。类型成员如果优先考虑公开及类型或接口,那么会让类型支持更多的应用场合。例如泛型单例。
97:优先考虑将基类型或接口作为参数传递。参考:里氏替换原则和面向接口编程
98:用params减少重复参数
99:重写时不应使用子类参数,只使用跟本类相关职责的参数。
100:静态方法和实例方法区别不大,静态方法在加载时机和内存使用上和实例方法完全一致,那它们之间唯一的区别就是,当我们需要使用实例方法的时候,首先应该有实例对象。
101:使用扩展方法,向现有类型“添加”方法
102:区分接口和抽象类的应用场合,前者为功能性添加,后者为通用性的归纳。
103:区分组合和继承的应用场合,组合优于继承。
104:用多态代替条件语句。参考策略模式
105:使用私有构造函数强化单例。
106:为静态类添加静态构造函数。使用静态构造方法的好处是,可以初始化静态成员并捕获在这过程中发生的异常。而使用静态成员初始化器则不能在类型内部捕获异常了。
107:区分静态类和单例。从本质上讲,在C#中,静态类不会被认为是一个“真正的对象”。而单例,则不会存在这样的问题。单例,它是一个实例对象,仅仅因为特殊的要求,它被自己实现为在整个系统中只有一个对象。
108:将类型标识为sealed,可以有效的防止使用者错误的继承
109:谨慎使用嵌套类
110:用类来代替enum,当然仅仅是轻量值类型,使用enum毫无问题。
111:避免双向耦合,也就是常说的互相持有、循环引用、孤岛的循环引用。虽然C#使用的是可达算法避免这种互相引用造成的无法释放GC的情况。但发现这种情况就应该立刻重构掉。
112:将现实世界中的对象抽象为类,将可复用对象圈起来就是命名空间
113:声明变量前考虑最大值
114:MD5不再安全,例如暴力破解。
115:通过HASH来验证文件是否被篡改
116:避免用非对称算法加密文件
117:使用SSL确保通信中的数据安全
118:使用SecureString保存密钥等机密字符串
119:不要使用自己的加密算法。除非你对密码学很在行
120:为程序集指定强名称。(几乎用不到)
121:为应用程序设定运行权限
122:以<Company>.<Component>为命名空间命名
123:程序集不必与命名空间同名
124:考虑在命名空间中使用复数
125:避免用FCL的类型名称命名自己的类型
126:用名词和名词组给类型命名。类型定义了属性和行为。虽然它包含行为,但不是行为本身。正确:OrderProcessor错误:OrderProcess
127:用形容词组给接口命名。接口规范的是“Can do”,也就是说,它规范的是类型可以具有哪些行为。所以,接口的命名应该是一个形容词,例如:IDisposable表示可以被释放IEnumerable表示类型含有Items,可以被迭代。
128:考虑让派生类的名字以基类名字作为后缀
129:泛型类型参数要以T作为前缀
130:以复数命名枚举类型,以单数命名枚举元素
131:用PascalCasing命名公开元素
132:考虑用类名作为属性名。不过笔者更倾向前缀加Get
133:用camelCasing命名私有字段和局部变量
134:有条件地使用前缀,轻量使用。
135: 考虑使用肯定性的短语命名布尔属性。public bool IsEnabled { get; set; }或public bool IsTabStop { get; set; }
136:优先使用后缀表示已有类型的新版本,且SampleClass1比SampleClassNew要好,因为后续可以添加为SampleClass2、SampleClass3等
137:委托和事件类型应添加上级后缀
138:事件和委托变量使用动词或形容词短语命名。例如:onClick
139:事件处理器命名采用组合方式。对象+下划线+事件变量名,如:button_Click、button_SizeChanged、button_MouseDown。委托中的回调,对象+On+委托变量名,如NameOnValidateValue,当然如果处理器在类内部,也可以是On+委托变量名。(笔者多数统一以On对象Handle的形式,OnClickHandle)
140:使用默认的访问修饰符。笔者为了看上去统一,默认的访问修饰符也会添加。
141:不知道该不该用大括号时,就用
142:总是提供有意义的命名。要知道读代码的时间要比写代码的所花费的时间要多得多。
143:方法抽象级别应在同一层次,可以理解为作用域的级别保持一致。
144:一个方法只做一件事
145:避免过长的方法和过长的类。对于方法在VS中需要滚屏才能阅读完,那么就肯定有些过长了,必须想办法重构它。对于类型,除非有非常特殊的理由,类型的代码不要超过300行。如果行数太多了,则要考虑是否重构。
146:只对外公布必要的操作。因为调用者可能会调用他所能调用的所有元素且不分先后顺序。
147:重构多个相关属性为一个类
148:不重复代码。DRY原则
149:使用表驱动法避免过长的if和switch分支,也就是有意义的变量最好用读表的形式。
150:使用匿名方法、Lambda表达式代替方法。Linq+Lambda表达式真的很爽。抛弃易用性过分的追求极致性能,对目前的开发环境毫无意义。
151:使用事件访问器替换公开的事件成员变量。(笔者保留意见)
152:最少,甚至是不要注释。让代码自行解释自身的行为。但绝不是说,添加注释是一个错误行为或者为自己不添加注释找理由。
153:若抛出异常,则必须要注释
154:不要过度设计,在敏捷中体会重构的乐趣。(够用就好)
155:随生产代码一起提交单元测试代码。
156:利用特性为应用程序提供多个版本。例如Unity Debug输出到屏幕并保存到本地中的Conditional("EnableLog")特性
157:从写第一个界面开始,就进行自动化测试。


更多技巧详见 Unity 之如何写出强壮的代码

推荐阅读更多精彩内容