NET设计规范:约定、惯用法与模式———命名规范
前言:
最近在看《.NET设计规范:约定、惯用法与模式》一书,主要还是讲.NET的设计规范,以前对这一块也不是特别在意,最近想要把这些系统的学习一下,以下基本上算是读书笔记吧。
第三章命名规范
3.1 大小写约定
使用合适的大小写可以使类型、成员以及参数的标识符更容易阅读
3.1.1 标识符大小写原则
为了区分一个标识符中的多个单词,把标识符中的每个单词的首字母大写,有以下两种合适的方法来大写标识符中的字母,如下:
l Pascal Casing
l Camel Casing
PascalCasing约定被用于除了参数名之外的所有标识符,它把标识符中的每个单词的首字母大写。如下所示:
PropertyDescriptor
Html
两个字母长的首字母缩写词是一个特例,在这种情况下两个字母都要大写,如下所示:
IOStream
Camel Casing约定仅用于参数的名字,它把标识符中除了第一个单词之外的所有单词的首字母都大写。如下所示:
propertyDescriptor
html
ioStream(标识符以两个字母长的的首字母缩写词开始,两个字母都要小写)
标识符基本大小写规范:
要把Pascal Casing用于多个单词构成的名字空间、类型以及成员的名字。
要把Camel Casing用于参数的名字。
不同类型的标识符的大小写规则如下:
3.1.2 首字母缩写词的大小写
避免在标识符的名字中使用首字母缩写词是很重要的,除非他们很常用,能够被使用框架的人立刻理解,如:HTML、XML等。
从定义上讲首字母缩写词必须至少有两个字母。由三个或以上的字母组成的首字母缩写词遵循与其他单词一样的规范:只有第一个字母大写,除非是camelCasing风格的参数名中的第一个单词,在这种情况下两个字母都小写。如下所示:
Public void StartIO(Stream ioStream,boolcloseIOStream)
Public void PorcessHtmlTag(string htmlTag)
要把两个字母的首字母缩写词全部大写,除非它是camelCasing风格的参数名的第一个单词
System.IO
Public void StartIO(StreamioStream)
要把由三个或三个以上字母组成的首字母缩写词的第一个字母大写。除非它是camelCasing风格的参数名的第一个单词
System.Xml
Public voidProcessHtmlTag(string htmlTag)
不要把camelCasing 风格的标识符头部的任何首字母缩写词的任何字母大写,无论首字母缩写词的长度是多少
3.1.3 复合词和常用术语的大小写
在涉及大小写时,大多数复合词术语要作为单个单词处理。
不要把所谓闭合形式的复合词中每个单词的首字母大写。
例如:endpoint,为了统一大小写规范,我们把闭合形式的复合词作为一个单词处理。
常用的复合词和常用术语的大小写及拼写,如下图所示:
上边的示例中有两个常用的的其他术语,Ok和Id,它们本身是一个类别,因为它们是俚语性质的常用缩写
3.1.4 是否区分大小写
不要以为所有的编程语言都是区分大小写的,实际情况并非如此,不应该仅仅通过大小写来区分名字。
3.2 通用命名约定
本章描述一些通用的命名约定,涉及到单词的选择、单词缩写、首字母缩写词的使用规范以及如何避免使用编程语言特有的名字。
3.2.1 单词的选择
对于框架中标识符的名字最重要的一点是一目了然。名字意思清楚比长度短更重要,应该与场景、系统的逻辑组成或物理组成以及为人熟知的概念相对应,不应该与技术活架构相对应。
主要有以下几点:
要为标识符选择易于阅读的名字。
要更看重可读性,而非简短性。
不要使用下划线、连字符以及其他任何既非字母也非数字的字符。
不要使用匈牙利命名法。
避免使用与广泛使用的编程语言的关键字有冲突的标识符。
3.2.2 使用单词缩写和首字母缩写词
不要使用缩写词和缩约词作为标识符名字的一部分。
不要使用未被广泛接受的首字母缩写词(即使是被广泛接受的首字母缩写词,也只应该在必须的时候才使用)
3.2.3 避免使用语言特有的名字
为了确保框架能够充分利用跨语言协作,避免在标识符中使用语言特有的类型名是重要的。
要给类型名使用语义上有意义的名字,而不要使用语言特有的关键字。
要使用CLR的通用类型名,而不要使用语言特有的别名----如果除了类型之外,标识符没有其他的语义。
要使用常见的名字,比如value或item,而不要重复类型的名字----如果除了类型之外,标识符没有其他的语义,而且参数的类型不重要。
3.2.4 为已有API的新版本命名
本节主要描述了当用新类型和新成员接替或取代已有的类型或成员时,如何为它们选择名字。
要在创建已有API的新版本时使用与旧API相似的名字,这有利于突出API之间的关系。
例如:class AppDommain{}
Class AppDommainSetup{}
要优先使用后缀而不是前缀来表示已有API的新版本。
考虑使用全新但有意义的标识符,而不是简单的给已有标识符添加后缀或前缀。
要使用数字后缀来表示已有API的新版本—如果已有API的名字时唯一有意义的名字,不宜添加后缀。
例如:publicclass X509Certificate{}
Public class X509Certificate2{}
不要在标识符中使用“Ex”后缀来区分相同API的不同版本。
例如:publicclass Car{}
Public class CarEx{} //错误的示例
3.3 程序集和DLL的命名
程序集是一个部署单元,同时还代表托管代码程序的身份。一般来说一个程序集仅与一个DLL相对应。本节主要讲DLL命名约定,程序集的命名约定与此类似。
要记住,名字空间与DLL和程序集是不同的概念。名字空间对开发人员来说是一组逻辑实体,而DLL和程序集则是在打包和部署时的一个单元。
要为程序集和DLL选择提示性的名字(例如:System.Data)这样很容易就知道它的大致功能。程序集和DLL的名字不一定要和名字空间相对应,但在程序集命名时遵循名字空间的名字也是合情合理的。
考虑按照如下的模式给DLL命名:
<Company>.<Component>.dll
其中<Component>包含一个或多个以点号分隔的字句,如:
Microsoft.VisualBasic.dll
Microsoft.VisualBasic.Vsa.dll
3.4 名字空间的命名
命名空间的目标是清晰,方便框架的使用者。一般的名字空间命名规则如下:
<Company>.(<Product>|<Technology>)[.<Feature>][.<Subnamespace>]
示例如下:
Microsoft.VisualStudio
Microsoft.VisualStudio.Design
Litware.Security
l 要用公司名称作为名字空间的前缀,这样就可以避免与另一家公司使用相同的名字。
l 要用稳定的、与版本无关的产品名称作为名字空间的第二层。
l 不要根据公司的组织架构来决定名字空间的层次结构,因为公司内部组织的名称一般来说不会持续太长的时间。
l 要使用PascalCasing大小写风格,并用点号来分隔名字空间中的各部分(例如:Microsoft.office.PowerPoint).如果商标使用了非传统的大小写风格,那么即使该风格与常规大小写风格相背,也还是应该遵循商标的大小写风格。
l 考虑在适当的时候在名字空间中使用负数形式。
例如:要用System.Collections而不要用System.Collection。商标和首字母缩写词除外。
l 不要用相同的名字来命名名字空间与位于该名字空间中的类型。
例如:不要将名字空间命名为Debug,然后又在该名字空间中提供一个名为Debug的类。
名字空间与类型名的冲突
名字空间用来把类型组织成一个逻辑的、易于浏览的层次结构。对解决在导入多个名字空间时可能引起的类型名的二义性,它们是不可或缺的。
不要引入太一般化的类型名(例如:Element,Node,Log,Message)
这样的名字很容易引起类型冲突,名字空间可以分为下面的类别:
l 应用程序模型名字空间(application model namespace)
l 基础设施名字空间(infrastructure namespace)
l 核心名字空间(core namespace)
l 技术名字空间组(technology namespace group)
1. 应用程序模型名字空间
不要给位于同一个应用程序模型的名字空间中的类型起相同的名字。
2. 基础设施名字空间
这个类别包含了一些再开发常用的应用程序时很少会导入的名字空间。
3. 核心名字空间
核心名字空间包含了所有的System名字空间,但应用程序模型名字空间和基础设置名字空间除外。(核心名字空间包括System、System.IO、System.Xml、System.Net等)
不要给类型会与核心名字空间中的任何类型产生冲突的名字。(例如:不要用Stream来作为类型名,它会和System.IO.Stream产生冲突)
4. 技术名字空间组
这个类别包括所有那些以相同的两个前缀(<Company>.<Technology>*)开始的名字空间(例如:Microsoft.Build.Utilities和Microsoft.Build.Tasks属于同一个技术类型,但是彼此之间不冲突)
不要给类型会与位于同一技术组中的其他类型产生冲突的名字。
不要在技术名字空间和应用程序模型名字空间的类型之间引入类型名冲突(例如:不应给Microsoft.VisualBasic名字空间增加一个Binding类型,因为System.Windows.Forms名字空间已包含该类型名)
3.5 类、结构和接口的命名
一般来讲,类型名应该是名词词组,因为他们代表了系统中的实体,如果无法为类型找到一个名词词组,那么可能应该重新考虑该类型的总体设计。
最易识别的名字应该用于最常用的类型,最常用的类型名应该反映出使用场景,而不是继承层次。大多数用户只是用继承层次的叶节点,而不关心类层次结构。
下面的命名规范适用于一般的类型命名。
要用名词或者名词词组来给类型命名,在极少数情况下也可以用形容词词组来给类型命名。(命名时用PascalCasing大小写风格,这使类型名和方法区分开,后者用动词词组来命名。)
不要给类名加前缀。(唯一的前缀是用于接口“I”)
考虑让派生类的名字以基类的名字结尾。(例如:publicclass FileStream:Stream{…})
要让接口的名字以字母I开头,这样可以显示出该类型是一个接口。(例如:IComponent)
要确保一对类/接口的名字只差一个“I”前缀,如果该类是该接口的标准实现。
例如: public interface IComponent{…}
Public class Component :IComponent{…}
3.5.1 泛型类型参数的命名
泛型是.NET框架2.0一个主要特征,它引入了一种称为“类型参数(type parameter)”的新标识符。以下的规范描述了与类型参数有关的命名约定。
要用描述性的名字来命名泛型类型参数,除非一个字母就够了,而且描述性的名字并不能增添什么价值。
例如:public interface TSessionChannel<TSession>{…}
Public class List<T>{…}
考虑用T来命名参数类型——如果类型只有一个类型参数,且类型参数只有一个字母。
例如:public int IComparer<T>{…}
Public delegate boolPredicate<T>(T item);
要给描述性的类型参数名加上T前缀。
例如:public interface ISessionChannel<TSession> whereTSession:ISession
{
TSession Session{get;}
}
考虑在类型参数名中显示出施加于该类型参数上的限制。
3.5.2 常用类型的命名
如果要从.NET框架所包含的类型派生新类型,或者要实现.NET框架中的类型,要遵循下表的一个命名规则。如下所示:
3.5.3 枚举类型的命名
一般来说,枚举类型的命名应该遵循标准的命名规则(PascalCasing 大小写风格)。但是还有一些针对枚举类型的具体规范。
要用单数名词来命名枚举类型,除非它表示的事位域(bit field)
要用复数名词来命名表示位域的枚举类型,这样的枚举类型也称为标记枚举(flag enum)
例如:[Flags]
Public enum ConsoleModifiers{
Alt,
Control,
Shift
}
不要给枚举类型的名字添加“Enum”后缀。
例如: public enum ColorEnum{…}//这样就不好
不要给枚举类型值的名字添加前缀。
例如:public enum ImageMode{
ImageModeBitmap=0,
ImageModeGrayScale=1,
ImageModeRgb=2
}
下边的命名会更好:
Public enum ImageMode{
Bitmap=0,
GrayScale=1,
Rgb=2
}
3.6 类型成员的命名
类型由方法、属性、事件、构造函数以及字段等成员组成。
3.6.1 方法的命名
因为方法是用来执行操作的,因此框架的设计规范要求方法名必须是动词或者动词词组。它还用来把方法同属性和类型名区分开,属性和类型名是名词或形容词词组。
要用动词或动词词组来命名方法。
3.6.2 属性的命名
与其他成员不同,属性应该用名词词组或形容词词组来命名。命名时要始终使用PascalCasing大小写风格。
要用名词、名词词组或形容词来命名属性。
不要让属性名看起来与“Get”方法的名字相似。
要用肯定性的短信(CanSeek而不是CantSeek)来命名布尔属性。如果有帮助,还可以有选择性的给布尔值属性添加“Is”,“Can”,“Has”等前缀。
考虑用属性的类型名来命名属性。
例如:public enum Color{…}
Public class Control{
Public Color Color{get{…} set{…}}
}
3.6.3 事件的命名
事件总是表示一些动作,要么正在发生,要么已经发生。因此和方法一样,要用动词来命名,此外,还要用动词的时态来表示事件发生的时间。
要用动词或动词短语来命名事件。(例如:Clicked,DroppedDown等)
要用现在时和过去时来赋予事件名以之前和之后的概念。
例如:在窗口关闭之前的close事件命名为Closing,关闭之后的事件命名为Closed
不要用“Before”或“After”前缀或后缀来区分前置和后置事件。
要在命名事件处理函数时加上“EventHandler”后缀。(例如:public delegate void ClickedEventHandler(object sender,ClickedEventArgse);
要在事件处理函数中用sender 和e作为两个参数的名字。
参数sender表示触发该事件的对象。
要在命名事件的参数类时加上“EventArgs”后缀。
3.6.4 字段的命名
字段的命名规范适用于静态共有字段和静态受保护字段。
要在命名字段时使用PascalCasing大小写风格。
要用名词或名词短语来命名字段。
不要给字段名添加前缀。(例如:不要用“g_”或”s_”来区分静态和非静态字段)
3.7 参数的命名
要在命名参数时使用camelCasing大小写风格。
要使用具有描述性的参数名。
参数名应该具有足够的描述性,在大多数情况下,用户根据参数名字和类型就能够确定它的意思。
考虑根据参数的意思而不是参数的类型来命名参数。
3.8 资源的命名
本地化的资源就好比是属性,可以通过特定的对象来引用。因此资源的命名规范与属性的命名规范相似。
要在命名资源关键字时使用PascalCasing大小写风格。
要使标识符的名字具有描述性而不是使名字变短。
不要使用各种CLR编程语言特有的关键字。
要在命名资源时仅使用字母、数字和下划线。
要用点号来给标识符清楚的划分层次。
例如:设计菜单系统资源,命名方式如下:
Menus.FileMenu.Close.Text
Menus.FileMenu.Close.Color
要在为异常的消息资源命名时遵循下面的命名约定:
资源标识符应该是异常的类型名加上一个简短的异常标识符,之间以点号分隔:
ArgumentException.IllegalCharacters
ArgumentException.InvalidName