|前言程序员
程序员其实艺术家,灵动的双手如行云流水般在键盘上创造着生命的奇迹,我认为代码是有灵魂的。同一个模块,在每一个程序员手中所缔造出来的是不相同的。算法
最终,这个模块或者实现了最初的业务,可是回过头看看你的做品,你会认为她是你的艺术品,仍是她就是一坨Code?编程
好吧,为了普及C#是最美的编程语言这种思想,我决定写这篇【THE ELEMENTS OF C# STYLE】,欢迎吐槽!设计模式
|通常原则 缓存
1. 坚持最小惊奇原则
简单性(Simplicity)
用简单的类和简单的方法知足用户指望
清晰性(Clarity)
确保每一个类、接口、方法、变量和对象都有清晰的目的,并阐明什么时候、何处、如何使用它们
完整性(Completeness)
提供任一可能的用户指望找到和使用的最小功能,建立完整的文档,描述全部特性和功能
一致性(Consistency)
类似实体的外观和行为应该相同,不一样实体的外观和行为应该不一样,应该尽量制定和遵照相关标准
健壮性(Robustness)
对软件中可能出现的错误和异常作出预测,并将解决方法记入文档,不要隐藏错误,也不要等着用户去发现错误
2. 第一次就作对
3. 记录全部规范的行为并发
|格式 编程语言
1. 第一次就作对工具
1.1. 使用快捷键 Ctrl + K + D 来格式化 .CS 代码
1.2. 在流程控制语句中使用语句块花括号
但若控制体中为如下单句语句,能够不使用花括号
return, break, continue, throw, yield return, gotopost
if(a<2) return;
2. 类的组织测试
2.1.使用 .CS 文件右键菜单中顶部的Organize Usings-> Remove and Sort 来整理 using 指令
2.2.使用 #region 将源代码组织到不一样区域中
推荐使用ReSharper的 File Structure 窗口方便的管理代码结构(快捷键Ctrl + Alt + F)
通常能够考虑按如下类型和访问级别组织类成员:
#region [Fields]
#region [Embedded Types]
… 这里放public 成员
#region [Protected]
#region [Private Methods]
区域尽可能不要嵌套多层
2.3.单独声明每一个变量和特性(每行只放一个)
不建议的书写方式:
int a;int b;int c;
|命名
1. 通常原则
1.1. 使用有意义的名称
应该使用对读代码的人来讲有意义的名称,避免使用单个字符或太通常性的名称(特别是在方法内部实现中)
1.2. 根据含意而非类型来命名
类型信息通常可从其用法和智能感知中看出来,如:使用Customer 而不是CustomerClass对于UI控件的命名能够加上控件的类型
如:CustomerNameLabel、 FeedBackButton
1.3. 使用熟悉的名称(目标领域通用的术语)
1.4. 不要用大小写来区分名称
虽然编译器区分大小写,但这样会影响代码可读性,如:XMLStream和XmlStream
1.5. 避免使用过长的名称
对象的名称应当足以描述其目的,若是名称过长,可能说明该实体企图实现的功能太多,此时应当考虑是否须要重构
1.6. 加上元音 – 使用完整的单词
不要经过去除元音来缩短名称,这样会下降代码可读性,有时还可能产生歧义
如:不要使用 public Msg AppendSig(),而应该使用 public Message AppendSignature()
1.7. 不要出现拼写错误,第一次就拼对!
2.缩略形式
2.一、除非全称太长,不然不建议使用缩略形式
不要使用没必要要的缩略词,这样会影响可读性,如:使用Attachment 而不是Atta
若是必须使用缩略词,请使用广为使用和接受的缩略词,如:Api、App、Zip
2.2. 像普通词同样书写缩略词
不要全都大写,如:XmlString 而不是 XMLString
3.类型和常量
3.1. 建议使用Pascal写法给命名空间、类、结构、属性、枚举、成员常量及方法命名
每一个单词首字母都大写,如:DataManipulator
3.2. 建议使用名词命名类、结构和属性
如: NetworkManager、 public string NetworkType { set; get; }
3.3. 建议用复数形式书写集合名称
集合对象的名称应该有能反映集合中对象类型的复数形式,
差:List<Shape> shapeList = new …
好:List<Shape> shapes = new …
3.4. 建议给抽象基类加上Base 后缀
如:public abstract class AccountBase
public class PersonalAccount: AccountBase
3.五、使用单个大写字母命名泛型参数
单个泛型参数使用<T>,多个泛型参数将T做为前缀,如:<TKey, TValue>
3.六、给实现了一种设计模式的类添加模式名称
如:public class MessageFactory
4.枚举
4.1. 用单数形式为(通常)枚举命名
如:public enum EnhancementMode
4.2. 用复数形式为位域枚举命名
如,定义时:
public enum Languages { English = 1, ChineseSimplify = 1 << 1, ChineseTraditional = 1 << 2, … }
使用时(一般与位或操做符一块儿使用):
var languages = Languages.English | Languages.ChineseSimplified | Languages.ChineseTraditional
5.接口
5.1. 用大写字母 I 做为接口名称的前缀(IInterface)名
5.2. 使用名词或形容词给接口命名
若接口声明了对象提供的服务,可用名词命名,如:public interface IMessageListener
若接口描述了对象的能力,可以使用带-able或-ible后缀的形容词命名
如:public interface IReversible
6.属性
6.1. 根据Getter和Setter的值来为属性命名
如对于一个取得过时日期的属性,将其命名为ExpirationDate
6.2. 避免冗长的属性名称
如:使用Icollection Customers,而不用Icollection CustomerCollection
6.3.用能体现布尔值特性的名称给bool类型的属性命名
视含意添加Is、Has等前缀,如:bool IsOpen, bool HasCompleted
7.方法
7.1. 使用Pascal写法为方法命名
每一个单词首字母都大写,如:ParseSecondsFrom1970
7.2. 避免冗长的方法名
操做类名对象的类的成员方法名中不须要再带有该类对象名
如:在Book 类中,使用方法Open(),而不是OpenBook()
8.变量和参数
8.1. 使用Camel写法为变量和参数命名
第一个单词的首字母小写,后面每一个单词的首字母大写,如:lastName
8.2. 用名词命名变量和参数
8.3. 给字段(Fields)名称加上下划线前缀_,使之与其余变量区分开来
如:_lastName
8.4. 用一系列标准名称为“一次性”变量和参数命名
•循环变量int i, j, k
•object o, obj
•string s, str
•Exception e, ex
•EventArgs args
9.特性
9.1. 给自定义特性加上Attribute后缀
定义时:
public class MyCustomAttribute: Attribute { … }
使用时:
[MyCustom]
public class CertainClass{ … }
10.事件、异常
10.1. 事件的名称应当包括对象及动做的描述
如:public event EventHandler<MessageReceivedEventArgs> MessageReceived
10.2. 事件对应的参数类型应当添加EventArgs后缀,并继承自EventArgs类
public class MessageReceivedEventArgs: EventArgs
{
//…
}
10.3. 事件所在的类中应当有一个名为OnEventName()的方法用于触发事件
10.4. 给自定义异常类型添加Exception 后缀
public MyCustomException: Exception
{
//…
}
|文档
1.通常原则
1.1. 为使用接口的人编写软件接口文档
编写代码中公共接口的文档,可让别人正确、高效地理解和使用接口
编写文档注释的首要目的是在服务的提供者(supplier)和客户(client)之间定义一种编程契约(programming contract)
与方法相关的文档应该描述与该方法的调用者有依赖的行为的诸多方面,而不该试图描述其实现细节
1.2.为维护者编写代码实现文档
老是假定会有彻底不熟悉你的代码的人要阅读和理解你的代码
1.3.保持注释和代码同步
修改代码时,也要确保更新相关注释
代码和文档一块儿构成软件产品,应对其同等重视
“When the code and the comments disagree, both are probably wrong.”–Norm Schryer, Bell Labs
1.4.尽早编写软件元素的文档
在实现以前或实现过程当中编写软件元素的文档,勿拖延至软件将近完成才编写,由于对代码太过熟悉或太过厌烦,在项目结尾作的文档每每缺乏细节
若是在实现以前编写软件参考文档,则可使用该文档为受托实现软件的开发人员定义需求
1.5.考虑全世界的读者
2.API
1.尽可能使用C#内建的文档机制
使用<summary>等标签编写文档,以后VS可自动生成API文档
使用NDoc等工具可自动生成可知足各类需求各类格式的文档
经常使用标签:
Ctrl + Shift + F1
3.内部代码
3.1. 只在须要帮助别人理解代码的时候才添加内部注释
3.2. 解释代码为何要这样作
3.3. 避免使用C风格的注释块 /* … */
3.4. 使用单行注释 // 描述实现细节
特定变量或表达式的目的
实现层面的设计决定
复杂算法的来源资料
缺陷的修正或变通方法
之后可能作优化或加工的代码
任何所知的问题、局限或缺陷
3.6. 使用关键词标出待完成工做、未解决问题、错误和缺陷修正
|编程
1.类型
1.1.使用内建的C#类型别名
别名更加简洁易读,并且具备关键字语法着色
例如:使用int,而不是System.Int32;string,而不是String;object,而不是Object
1.2. 避免使用内联字面值
不要:if (size > 45)
而是:const int limit = 45; if (size > limit)
这样不但改善了可读性,并且便于在一个位置修改
1.3.避免没必要要的值类型装箱拆箱
1.4. 使用标准形式书写浮点字面值
差:const double foo = 0.000042;
好:const double foo = 4.2e-5;
1.5. 将“值”语义定义为struct(而不是class)
选用struct表示其实例在堆栈上建立
注意struct不能继承和派生(是密封的),并且没有默认的构造器
1.6. 避免使用代价较高的隐藏式字符串分配
代码if (str1.ToUpperCase() == str2.ToUpperCase()) 将产生2次字符串分配
能够优化为if (string.Compare(str1, str2, true))
1.7. 采用更高效的空字符串检测方法
差:if (str == “”)
好:string.IsNullOrEmpty(str) / string.IsNullOrWhiteSpace(str)
1.8. 只在必要时使用可空值类型
由于须要装箱拆箱,因此请确认只在有必要的状况下使用可空值类型
2.语句和表达式
2.1. 在复杂表达式中使用括号,而不要依赖操做符优先级
差:varj = 10 * 2 << 1 + 20;
好:varj = (10 * (2 << 1)) + 20;
2.2. 不要将布尔值与true 或false 进行相等比较
if (popup.IsOpen == true) 这没有必要!应当老是直接判断布尔值if (popup.IsOpen)
2.3. 使用静态方法object.Equals() 测试引用类型等同
由于某个类型的实例方法Equals() 和操做符 == 可能已被重写
if (object.Equals(str1, str2))
3.类
3.1. 定义小类和小方法
较小的类和方法易于设计、编码、测试、编写文档、阅读、理解和使用,较小的类一般拥有更少的方法,能表达更简单的概念
尽可能将每一个类的接口(指对外暴露的功能)限制在提供必要功能所需的最少法以内
尽可能将较大的方法拆分出一些较小的私有方法,即便这些小方法只被调用一次,由于切割开的代码更容易阅读和重用
另外,CLR能够对小方法进行更好的代码优化
3.2. 声明全部成员的访问级别
不要假设别人会记得默认的访问级别,应当所有写出,并按原则上public, protected, private 的顺序排列
3.3. 合理避免使用internal 声明
internal成员一般标志着很差的设计,由于它绕过了访问限制,并且隐藏了类和成员之间的关系
你能说出protected internal 的访问级别吗?
仅在如下状况下考虑使用internal:
•某工具类仅提供给同程序集中的其余类使用,不须要暴露给程序集的使用者
•须要防止派生类得到特定父类方法,但又同时容许特定辅助类等访问这些方法
4.字段、属性、方法
4.1. 声明全部字段为private,使用属性提供访问
将实现细节看做私有信息,下降在实现改动时对依赖类的影响,而且让数据持续有效
4.2. 只为简单、低成本、顺序无关的访问使用属性
即不该有反作用、开销大、与访问顺序有关
4.3. 方法中避免传递过多参数
若是某个方法须要大量参数,就应该考虑是否要从新设计了,参数太多标志着方法作了太多的事,或者数个相关参数能够被抽象到另外一个类里
5.异常
5.1. 使用返回码(return code)报告预期的状态改变
5.2. 使用异常强迫得到编程契约
•前置条件异常(pre-conditional exception):参数无效或调用方法时相关对象处于无效状态
•后置条件异常(post-conditional exception):方法产出结果无效或在返回前相关对象处于无效状态
与方法调用者有关时才抛出异常,若是是内部逻辑错误,则应使用断言(Debug.Assert)
5.3. 不要静默地接受或忽略非预期的运行时错误
不要使用空的catch 块消除异常
5.4. 只捕获能处理的异常
合理避免使用通用的catch (Exception ex) { … }
5.5.使用try…finally… 代码块或using 语句管理资源
5.6.尽量抛出最具体的异常
NullReferenceException, ArgumentOutOfRangeException, FileNotFoundException
5.7.按照异常类型的特殊性级别排列catch 块
先捕获更特殊的异常
5.8.不要在finally 块中抛出异常
在finally 块中抛出异常会致使同时激活多个异常,很难处理
6.效率
6.1.使用懒惰求值和懒惰初始化(lazy evaluation & lazy initialization)
原则:
在须要结果以前,不要进行复杂的计算
老是在最靠近嵌套边缘的地方执行计算
若是可能,缓存结果
在须要对象以前,不构造对象(可能须要加锁来防止并发初始化)
6.2.重用对象以免再次分配
缓存并重用频繁建立且生命周期有限的对象
使用访问器而不是构造器来从新初始化对象
使用工厂模式来封装缓存和重用对象机制
6.3.避免建立没必要要的对象
特别是新对象生命周期较短,或者构造后历来不引用时,尤为须要注意
在知道本身须要什么以前,避免建立对象
6.4.让CRL处理垃圾回收
通常而言,避免调用GC.Collect() 方法,让垃圾回收器自行动做
在多数状况下,垃圾回收器的优化引擎比你更善于判断执行回收的最佳时机
6.5.到最后再进行优化
优化的第一原则:
不要优化
优化的第二原则(只针对专家):
仍是不要优化
在确认须要优化以前,不要花时间作优化
确实要作优化时,应采用80-20原则:平均而言,系统中20%的代码使用80%的资源,确保从这20%的代码开始优化
|推荐
神器Resharper很是好用,推荐安装,让你爱上Coding!