本文档在《C#程序设计与编码规范》2005年版(Old_Backup/编码规范目录)内的基础上缩编修订而成。更多的.net设计知识,请参阅微软出版的相关指南。
-
Pascal大小写
将标识符的首字母和后面连接的每个单词的首字母都大写。例如:BlackColor。
-
Camel大小写
标识符的首字母小写,而每个后面连接的单词的首字母都大写。例如:blackColor。
-
大写
标识符中的所有字母都大写。仅对于由两个或者更少字母组成的标识符使用该约定。例如:IO。
-
Private Camel大小写
标识符首字母为下划线”_”,后面单词采用Camel大小写。例如:_blackColor。
当标识符由多个单词组成的时候,不要在词间使用诸如”_”(Unix C程序样式 )和”-“之类的分隔符。而要通过大小写来分隔单词。
下面给出一般标识符的大小写规则:
下表汇总了大写规则,并提供了不同类型的标识符的示例:
标识符 | 大小写 | 例子 |
---|---|---|
名字空间 | Pascal | System.Drawing |
类 | Pascal | AppDomain |
接口 | Pascal | IDisposable |
枚举类型 | Pascal | ErrorLevel |
枚举值 | Pascal | FatalError |
方法 | Pascal | ToString |
属性 | Pascal | BackColor |
事件 | Pascal | ValueChanged |
只读静态字段 | Pascal | RedValue |
参数 | Camel | typeName |
局部变量 | Camel | targetCount |
私有字段 | Private Camel | _brushColor |
首字母缩写(Acronyms)是由短语内各单词的字母构成。例如:HTML是Hypertext Markup Lanugage的首字母缩写。只有众所周知的首字母缩写才可以包含在标识符里。首字母缩写(Acronyms)与缩写词(Abbreviation)不同,缩写词(Abbreviation)仅简化一个单词。例如:ID是identifier的缩写词。一般情况下,不要使用缩写词。
特例:ID及OK两个缩写词可被用于标识符。但是应该依据使用环境,遵循Pascal或Camel大小写。
首字母缩写的大小写依赖于它的长度。所有的首字母缩写至少两个字母长。如果正好是两个字母长,被称作短首字母缩写(short acronyms)。三个或三个以上长度的首字母缩写,被称作长首字母缩写(long acronyms)。
下面给出长短首字母缩写的规则,标识符的大小写优先级更高。
例如:属性名称DBRate中首字母缩写DB,是Pascal大小写标识符首单词。
例如:参数名称ioChannel中首字母缩写io,是Camel大小写标识符首单词。
例如:类名称XmlWriter中的首字母缩写Xml,是Pascal小写标识符首单词。
例如:参数名称htmlWriter中的首字母缩写html,是Camel大小写标识符首单词。
例如:hashtable是一个封闭形式的,从而应当作为单独的词来对待及处理大小写。采用Pascal大小写,它是Hashtable;采用Camel大小写,它是hashtable。
下面一些通用术语不是封闭形式的复合词,采用Pacal及Camel两种方式展现。
- BitFlag (bitFlag)
- FileName (fileName)
- LogOff (logOff)
- LogOn (logOn)
- SignIn (signIn)
- SignOut (signOut)
- UserName (userName)
- WhiteSpace (whiteSpace)
此部分是用于所有标识符命名,后面的章节将讨论特殊的元素,如名字空间及属性。
比如:属性名称HorizontalAlignment比AlignmentHorizontal在英语中更可读。
比如:属性名称CanScrollHorizontally比ScrollableX更好。
一般情况下不应该使用缩写,这样会使程序不容易读,另外也很难确定缩写是否广为人知。
例如:使用OnButtonClick而不是OnBtnClick
例如:使用GetLength而不是GetInt
例如:将数据转换成Int16类型的函数,应该被命名为ToInt16,而不是ToShort,因为short仅仅是Int16在C#语言中的对应类型名。
采用如下方式命名:
WanFangData.<Component>.dll
其中Component包含一个或多个点分隔的字句。
例如:WanFangData.WebContorls.dll
名字空间的名称,应能提示在此名字空间内的所有类型的主要功能。比如System.Socket名字空间所包含的类型,能够让程序员通过Socket方式进行网路通讯编程。
一般的命名规则是:
WanFangData.(<ProductName>|<Technology>)[.<Feature>][.<Subnamespace>]
例如:Micorsoft.WindowsMobile.DirectX
例如:应写System.Collections而不是System.Collection。
例如:应写System.IO而不是System.IOs。(缩写)
例如:不要在一个名为Debug的名字空间中同时提供一个名为Debug的类。
应尽量避免与已经存在的类库有名字冲突,否则用户将不得不改变程序,并使用限定名称。下面的讨论将与四类名字空间有关:
-
应用模型名字空间
诸如System.Windows.Forms,System.Web等与应用密切相关的名字空间。不同的应用很少同时在一个程序内使用,因此不容易出现冲突。
-
基础名字空间
基础应用提供提供特殊支持,并很少在代码中引用。比如:.Designer,.Permissions名字空间。很难出现冲突。
-
核心名字空间
核心名字空间是System.*名字空间。开发人员应该尽力,不产生名字冲突。(可以查Document,以识别此名称是否已经存在)
-
技术名字空间
技术名字空间一般为Company.Technology.*,应避免与其冲突。
例如:Element,Node,Log及Message等。这样很容易产生冲突。
限定后:FormElement,XmlNode,EventLog,SoapMessage。
例如:为Windows Form应用的用户写的控件库,请不要将类型命名为CheckBox,因为此名称已经存在于Windows Form应用中。
例如:不要使用Dictionary类型名称,因为它在核心名字空间里存在。
例如:从Stream继承的类,采用Stream作为后缀。
例如:从Exception继承的类,采用Exception作为后缀。
例如:IAsynResult的默认实现名称为AsynResult。
例如:IDictionary<TKey,TValue>。
例如:TKey。
例如:参数限制为ISession的,应被命名为TSession。
下面规则,通过前后缀,帮助开发人员识别类的特定使用情景。
例如:ObsoleteAttribute。
例如:AssemblyLoadEventHandler
例如:不要写成如下形式
public enum Teams
{
TeamsAlpha,
TeamsBeta,
TeamsDelta
}
应该写为:
public enum Teams
{
Alpha,
Beta,
Delta
}
应从使用者的角度命名,选择描述函数做什么的动词,而不是怎么做的动词。
例如:属性名为EmployeeRecord,同时有个方法名为GetEmployeeRecord。使用者将无从选择。
例如:使用CanSeek而不是CantSeek。
例如:属性类型为CacheLevel,其名称也为CacheLevel。
例如:在窗口关闭之前的事件,被称为Closing。
例如:在窗口关闭之后的事件,被称为Closed。
为了增强程序清晰新、可读性,应遵循以下规定。
例如:Function(param1, param2, param3);
有时候在同一个方法中有明显的程序功能归组情况,而又不需要划分到单独的方法,此时应当用空行加以区分,并加注释。
行数过多的代码不易于阅读,这也说明了模块化分可能存在问题。应该考虑将一个大的方法体拆分为更小的方法,以增加代码的清晰性。
过长的代码行不易于阅读,当一行的确需要写很长时,应当折行书写。
有时需要一次跳出多层循环,使用goto更为清晰。
主体逻辑代码嵌套过深,不易于阅读及理解。这也说明了逻辑或模块化分可能存在问题。应考虑修改逻辑,或将嵌套内的程序划分到一个更小的方法中。以增加清晰性。
不要采用C或Pascal等语言在函数开头声明参数的方式。应该在使用时声明。
同一个类型的不同方法中出现大块的重复性代码,说明模块划分出现了问题,应当将重复性的代码划分到一个更小的方法中。以增加清晰性。
你可以捕获上述的异常,如果捕获的目的是重新抛出异常,或将异常交给其他线程处理。下面展示了一个错误处理的例子:
public class BadExceptionHandlingExample1
{
public void DoWork()
{
// 做一些可能抛出异常的工作
}
public void MethodWithBadHandler()
{
try
{
DoWork();
}
catch (Exception e)
{
// 淹没了异常并继续执行
}
}
}
应用程序不应淹没可能造成意外状态的异常,如果你不能确认所有异常造成的可能结果,你应当让程序终止而不是淹没异常。
你应该只捕获那些你可以恢复的异常。例如:打开不存在文件造成的FileNotFoundException可以被应用程序处理,因为它可以将问题告知用户,并让用户设置其他文件名。而打开文件请求抛出的ExcutionEngineException不应当被处理,因为低层发生的问题无法被知晓,并且应用程序也不能确认继续运行是否安全。
捕获无法正确处理的异常,会隐藏重要的调试信息。
catch的目的是为了让你处理异常(例如,记录非致命错误)。finally的目的是为了让你执行清理代码而不管异常是否抛出。如果你分配了有限的资源,比如数据库联接或stream,你应当将释放它们的代码放在finally块中。
例如:
public void MethodWithBadCatch(Object anObject)
{
try
{
// 引起异常的方法
DoWork(anObject);
}
catch (ArgumentNullException e)
{
System.Diagnostics.Debug.Write(e.Message);
// 这样是错误的
// 会造成跟踪堆栈指向此处为错误地址,而非指向DoWork。
throw e;
// 应该直接写成
// throw;
}
}
例如:
if (password == “12345”) // 这是超级密码,通过后拥有管理员权限
例如:
// 添加用户
Line1;
Line2;
// 添加用户组
Line1;
Line2;
例如:
if (a > b)
{
......
} // end if (a > b)
例如:一组函数都是和用户帐务操作有关的。
#region 用户帐务操作相关函数
Function1;
......
#endregion
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
例如:
log.Debug(“简单字符串”); // 不需要判断
例如:
if (log.IsDebugEnabled) // 有字符串等操作,需要判断
log.Debug("成功添加用户:" + user.UserID);
- Fatal级:程序重大错误,以致无法启动或继续运行。
- Error级:程序本身出错,或加载资源错误。注意此错误不是用户造成的错误,比如输入密码错。
- Warning级:用户的一些错误可以记录到此级别,比如用户名密码输入错误。
- Info级:程序运行时,会将log开到这个级别。此级别记录让用户看到的日志信息。比如程序启动,重要配置文件加载,访问某个页面等较大的事件。
- Debug级:琐碎的方法调用过程记录。诸如方法进入、返回,方法内的重要事件。
例如:log.Debug(“MethodName 开始准备数据表格”);
例如:
public void AddUser( string userName, string password)
{
if (log.IsDebugEnabled)
log.Debug(“AddUser调用 username:” + userName + “, password:” + password);
......
}
98.公共(public)方法返回前,记录日志,标明此方法结束并记下返回结果。如果返回内容过于复杂,比如为DataSet,可以简单记录其特征(记录数)。如果没有返回结果,比如void方法,直接标明返回即可。(即返回时记录。)
例如:
log.Debug(“AddUser 成功返回”);
例如:
catch(Exception ex)
log.Debug(“AddUser出错”, ex);
此条规则可以帮助我们迅速定位第三方错误,而不会因为两方都未正确记录日志而纠缠不清。