因为CLR支持许多编程语言,这些语言有的区分大小写,有的不区分,所以不应该只用大小写来区分名字。但是,大小写对增强名字可读性的重要性怎么强调也不过分。以下的规范展示了一种使用大小写的简单方法,如果一致地使用,那么会使类型、成员以及参数的标识符更易于阅读。
为了区分一个标识符中的多个单词,把标识符中的每个单词的首字母大写。不要用下划线来区分单词,或者在标识符中的任何地方使用下划线。有两种合适的方法来大写标识符中的字母,这取决于所使用的标识符:
PascalCasing
camelCasing
PascalCasing约定被用于除了参数名之外的所有标识符,它把标识符中每个单词的首字母(包括长度为两个字符以上的首字母缩写词)大写,如下面的例子所示:
PropertyDescriptor
HtmlTag
两个字母长的首字母缩写词是一个特例,在这种情况下两个字母都要大写,如下面的标识符所示:
IOStream
camelCasing约定仅用于参数的名字,它把标识符中除了第一个单词之外的所有单词的首字母大写,如下面的例子所示。在例子中,如果camelCasing风格的标识符以两个字母长的首字母缩写词开始,那么两个字母都要小写。
propertyDescriptor
ioStream
htmlTag
下面是一些用于标识符的基本大小写规范。
√ 要把PascalCasing用于由多个单词构成的名字空间、类型以及成员的名字。
例如,要使用TextColor而不是Textcolor或Text_color。单个单词(比如Button)直接首字母大写。始终作为一个单词的复合词(比如endpoint)视为一个单词,只有第一个字母大写。
√ 要把camelCasing用于参数的名字。
表1-1描述了用于不同类型的标识符的大小写规则。
表1-1 不同类型的标识符的大小写规则
表格 18888
一般来说,避免在标识符的名字中使用首字母缩写词是很重要的,除非它们是普遍使用的,能够立刻被使用框架的人所理解。例如,HTML、XML和IO很容易理解,但不怎么常用的首字母缩写词绝对应该避免。
从定义上讲,首字母缩写词必须至少有两个字母。由三个或以上的字母组成的首字母缩写词遵循与任何其他单词一样的规范。只有第一个字母大写,除非是camelCasing风格的参数名中的第一个单词,在这种情况下第一个单词全部小写。
正如前面一节已经提到的,对两个字母组成的首字母缩写词(例如IO)的处理是不同的,其主要目的是为了避免混淆。这样的首字母缩写词的两个字母都应该大写,除非是camelCasing风格的参数名中的第一个单词,在这种情况下两个字母都小写。下面的例子列出了所有这些情况:
public void StartIO(Stream ioStream, bool closeIOStream);
public void ProcessHtmlTag(string htmlTag)
√ 要把两个字母的首字母缩写词全部大写,除非它是camelCasing风格的参数名的第一个单词。
System.IO
public void StartIO(Stream ioStream)
√ 要把由三个或三个以上字母组成的首字母缩写词的第一个字母大写。只有第一个字母大写,除非首字母缩写词是camelCasing风格的标识符的第一个单词。
System.Xml
public void ProcessHtmlTag(string htmlTag)
× 不要把camelCasing风格的标识符头部的任何首字母缩写词的任何字母大写,无论首字母缩写词的长度是多少。
在涉及大小写时,大多数复合词术语要作为单个单词处理。
× 不要把所谓闭合形式的复合词中每个单词的首字母大写。
这些复合词要写成一个单词,比如endpoint。为了统一大小写规范,我们把闭合形式的复合词作为一个单词处理。如果想知道一个复合词是否应该以闭合形式来写,可以借助最新的英语词典。
表1-2列出了一些最为常用的复合词和常用术语的大小写。
表1-2 常用的复合词和常用术语的大小写及拼写
Pascal | Camel | Not |
BitFlag | bitFlag | Bitflag |
CallBack | callback | CallBack |
Canceled | canceled | Cancelled |
DoNot | doNot | Dont |
Endpoint | endpoint | EndPoint |
FileName | fileName | Filename |
Gridline | gridline | GridLine |
Hashtable | hashtable | HashTable |
Id | id | ID |
Indexes | indexes | Indices |
LogOff | logOff | LogOut |
LogOn | logOn | LogIn |
Metadata | metadata | MetaData, metaData |
Multipanel | multipanel | MultiPanel |
Multiview | multiview | MultiView |
Namespace | namespace | NameSpace |
Ok | ok | OK |
Pi | pi | PI |
Placeholder | placeholder | PlaceHolder |
SignIn | signIn | SignOn |
SignOut | signOut | SignOff |
UserName | userName | Username |
WhiteSpace | whiteSpace | Whitespace |
Writable | writable | Writeable |
有两个常用的其他术语,它们本身属于另一个类别,因为它们是俚语性质的常用缩写。这两个单词是Ok和Id(它们的大小写应该如所显示的那样),虽然前面的规范说过名字中不应该使用缩写,但它们是例外。
虽然运行于CLR之上的一些编程语言的确区分大小写,但这并不是必需的。即使你使用的语言区分大小写,可能会使用框架的其他语言却并不一定会区分。因此,任何外部可访问的API不应该仅通过大小写来区分位于同一个上下文中的两个名字。
实际上是否区分大小写只有以下一条规范,但是很重要。
× 不要以为所有的编程语言都是区分大小写的,实际情况并非如此。不应该仅仅通过大小写来区分名字。
描述了一些通用的命名约定,它们涉及到单词的选择、单词缩写和首字母缩写词的使用规范以及如何避免使用编程语言特有的名字。
对框架中标识符的名字来说,很重要的一点是要一目了然。标识符的名字应该清楚地说明每个成员做什么,以及每个类型和参数表示什么。为此,名字的意思清楚要比长度短更重要。名字应该与场景、系统的逻辑组成或物理组成以及为人熟知的概念相对应,而不应该与技术或架构相对应。
√ 要为标识符选择易于阅读的名字。
例如,一个命为HorizontalAlignment的属性就比AlignmentHorizontal更易于阅读。
√ 要更看重可读性,而不是更看重简短性。属性名CanScrollHorizontally要胜过ScrollableX(不太明显地引用到了X坐标轴)。
× 不要使用下划线、连字符以及其他任何既非字母也非数字的字符。
× 不要使用匈牙利命名法。
× 避免使用与广泛使用的编程语言的关键字有冲突的标识符。
根据CLS(公共语言规范)的第4条规则,所有符合规范的语言必须提供一种机制,允许用户访问以该语言的关键字来命名的标识符。例如,C#在这种情况下使用@符号作为转义机制(escape mechanism)。但是,由于在使用方法时,用转义序列(escape sequence)要比不用转义序列麻烦得多,因此避免使用常见的关键字仍然是个好习惯
一般来说,不要在标识符中使用单词缩写或首字母缩写词。正如前面所讲,对名字来说,可读性比简短更为重要。另外有一点也同样重要,即不要使用未被广泛接受的单词缩写和首字母缩写词,也就是说,大多数并非领域专家的人能够立刻知道它们是什么意思。
√ 不要使用缩写词和缩约词作为标识符名字的一部分。例如,要用GetWindow,而不要用GetWin。
√ 不要使用未被广泛接受的首字母缩写词,即使是被广泛接受的首字母缩写词,也只应该在必需的时候才使用。
例如,UI用来表示User Interface,HTML用来表示Hypertext Markup Language。虽然许多框架设计师认为一些新近的首字母缩写词很快就会被广泛接受,但在框架的标识符中使用它们仍是不好的做法
对那些所谓的基本类型,CLR平台上的编程语言通常都有自己的名字(别名)来称呼它们。例如,int是C#中System.Int32的别名。为了确保框架能够充分利用跨语言协作——CLR的核心特性之一,避免在标识符中使用语言特有的类型名是重要的。
√ 要给类型名使用语义上有意义的名字,而不要使用语言特有的关键字。
例如,GetLength这个名字比GetInt要好。
√ 要使用CLR的通用类型名,而不要使用语言特有的别名——如果除了类型之外,标识符没有其他的语义。
例如,一个把类型转换为System.Int64的方法应该被命名为ToInt64,而不是ToLong(因为System.Int64是CLR类型名,它对应于C#特有的别名long)。表3-3列出了有别名的基本类型在CLR中的类型名(以及与C#、Visual Basic、C++相对应的类型名)
表3-3 语言特有的类型名及对应的CLR类型名
√ 要使用常见的名字,比如value或item,而不要重复类型的名字——如果除了类型之外,标识符没有其他的语义,而且参数的类型不重要。
下面是一个很好的例子,类提供的这些方法可以把各种不同的数据类型写入流中:
void Write(double value);
void Write(float value);
void Write(short value);
有时无法将一个新特性添加到一个已有的类型中去,即使已有类型的名字意味着它是新特性的最佳归宿。在这种情况下需要添加一个新类型,但由于要给新类型找一个好的新名字,因此这通常给框架设计师出了难题。类似地,在需要提供额外的功能时,经常会无法对已有的成员进行扩展或重载,为此,只能用新名字来添加新成员。下面的规范描述了当用新类型和新成员接替或取代已有的类型或成员时,如何为它们选择名字。
√ 要在创建已有API的新版本时使用与旧API相似的名字。
这有助于突出API之间的关系。
class AppDomain {
[Obsolete("AppDomain.SetCachePath has been deprecated. Please
use AppDomainSetup.CachePath instead.")]
public void SetCachePath(String path) { … }
}
class AppDomainSetup {
public string CachePath { get { … }; set { … }; }
}
√ 要优先使用后缀而不是前缀来表示已有API的新版本。
这有助于在浏览文档或使用Intellisense时发现新版本。由于大多数浏览器和Intellisense按字母的顺序显示标识符,因此旧API与新API在位置上会非常接近。
√ 考虑使用全新但有意义的标识符,而不是简单地给已有标识符添加后缀或前缀。
√ 要使用数字后缀来表示已有API的新版本——如果已有API的名字是唯一有意义的名字(也就是说,它是一个工业标准),不适宜添加后缀(或改名)。
// old API
[Obsolete("This type is obsolete. Please use the new version of the same class,
X509Certificate2.")]
public class X509Certificate { … }
// new API
public class X509Certificate2 { … }
× 不要在标识符中使用“Ex”(或类似的)后缀来区分相同API的不同版本。
[Obsolete("This type is obsolete. …")]
public class Car { … }
// new API
public class CarEx { … } // the wrong way
public class CarNew { … } // the wrong way
public class Car2 { … } // the right way
public class Automobile { … } // the right way
√ 要在引入对64位整数(long)而非32位整数进行操作的新版API时使用“64”后缀。只有当已经存在32位的API时才需要采用这种方法,对只有64位版本的全新API则不需要这样做。
举个例子,System.Diagnostics.Process中有许多API返回Int32值来表示内存大小,比如PagedMemorySize或PeakWorkingSet。为了在64位系统上也支持这些API,一些有相同名字但是带“64”后缀的API被加了进来。
public class Process {
// old APIs
public int PeakWorkingSet { get; }
public int PagedMemorySize { get; }
// …
// new APIs
public long PeakWorkingSet64 { get; }
public long PagedMemorySize64 { get; }
}
程序集是一个部署单元,同时还代表托管代码程序的身份。虽然程序集可以分布在一个或多个文件中,但一般来说一个程序集仅与一个DLL相对应。因此,本节只讨论DLL的命名约定,程序集的命名约定与此类似。
程序集是一个部署单元,同时还代表托管代码程序的身份。虽然程序集可以分布在一个或多个文件中,但一般来说一个程序集仅与一个DLL相对应。因此,本节只讨论DLL的命名约定,程序集的命名约定与此类似。
要记住,名字空间与DLL和程序集是不同的概念。名字空间对开发人员来说是一组逻辑实体,而DLL和程序集则是在打包和部署时的一个单元。DLL可以因产品的组织及其他原因而包含多个名字空间。由于名字空间的组织方式与DLL不同,因此应该单独设计。例如,如果决定把DLL命名为MyCompany.MyTechnology,这并不意味着DLL必须包含名为MyCompany. MyTechnology的名字空间。
√ 要为程序集和DLL选择提示性的名字,比如System.Data,这样很容易就知道它的大致功能。程序集和DLL的名字不一定要和名字空间相对应,但在给程序集命名时遵循名字空间的名字也是合情合理的。
√ 考虑按照下面的模式给DLL命名:
<Company>.<Component>.dll
其中<Component>包含一个或多个以点号分隔的子句,例如:
Microsoft.VisualBasic.dll
Microsoft.VisualBasic.Vsa.dll
Fabrikam.Security.dll
Litware.Controls.dll
与其他的命名规范一样,为名字空间命名的目标是清晰,这样对使用框架的程序员来说,他们就能立刻知道一个名字空间中大概有些什么。下面的模板给出了名字空间命名的一般规则:
<Company>.(<Product>|<Technology>)[.<Feature>][.<Subnamespace>]
下面是一些例子:
Microsoft.VisualStudio
Microsoft.VisualStudio.Design
Fabrikam.Math
Litware.Security
√ 要用公司名称作为名字空间的前缀,这样就可以避免与另一家公司使用相同的名字。例如,微软提供的Microsoft Office自动化API应该放在Microsoft.Office名字空间中。
√ 要用稳定的、与版本无关的产品名称作为名字空间的第二层。
× 不要根据公司的组织架构来决定名字空间的层次结构,因为公司内部组织的名称一般来说不会持续太长的时间。
√ 要使用PascalCasing大小写风格,并用点号来分隔名字空间中的各部分(例如Microsoft.Office.PowerPoint)。如果商标使用了非传统的大小写风格,那么即使该风格与常规的大小写风格相背,也还是应该遵循商标的大小写风格。
√ 考虑在适当的时候在名字空间中使用复数形式。
例如,要用System.Collections,而不要用System.Collection。但是,商标名称和首字母缩写词例外。例如,要用System.IO,而不要用System.IOs。
× 不要用相同的名字来命名名字空间与位于该名字空间中的类型。
例如,不要先将名字空间命名为Debug,然后又在该名字空间中提供一个名为Debug的类。许多编译器都要求用户在使用这样的类型时要加上完整的限定符。
这些规范涵盖了名字空间命名的一般规范,下一节将为某些特殊的子名字空间提供一些有针对性的规范。
名字空间用来把类型组织成一个逻辑的、易于浏览的层次结构。对解决在导入多个名字空间时可能引起的类型名的二义性,它们同样是不可或缺的。但即便如此,对那些通常会在一起使用的不同名字空间来说,仍不应该以此为借口在类型之间引入二义性。在常见的场景中,开发人员应该不需要给类型名加上限定符。
不要引入太一般化的类型名,比如Element、Node、Log以及Message。
这样的名字很可能会在常见的场景中引起类型名的冲突。应该给这些一般化的类型名加上修饰语(FormElement、XmlNode、EventLog、SoapMessage)。
对不同类别的名字空间,有具体的规范来避免类型名的冲突。名字空间可以分为下面的类别:
l 应用程序模型名字空间(application model namespace)。
l 基础设施名字空间(infrastructure namespace)。
l 核心名字空间(core namespace)。
l 技术名字空间组(technology namespace group)。
1. 应用程序模型名字空间
属于单个应用程序模型的名字空间经常在一起使用,但是它们几乎从不和属于其他应用程序模型的名字空间一起使用。例如,System.Windows.Forms名字空间很少和System.Web.UI名字空间一起使用。下面列出了众所周知的应用程序模型的名字空间组。
System.Windows*
System.Web.UI*
×不要给位于同一个应用程序模型的名字空间中的类型起相同的名字。
例如,不要给System.Web.UI.Adapters名字空间增加一个名为Page的类型,因为System.Web.UI名字空间已经包含了一个名为Page的类型。
2. 基础设施名字空间
这个类别包含了一些在开发常用的应用程序时很少会导入的名字空间。例如,.Design名字空间主要在开发编程工具时使用。避免和这些名字空间中的类型产生冲突并不是至关重要的。
System.Windows.Forms.Design
*.Design
*.Permissions
3. 核心名字空间
核心名字空间包括了所有的System名字空间,但应用程序模型名字空间和基础设施名字空间除外。核心名字空间包括System、System.IO、System.Xml以及System.Net等等。
× 不要给类型起会与核心名字空间中的任何类型产生冲突的名字。
例如,绝对不要用Stream来作类型名。它会和System.IO.Stream产生冲突,而后者是一个很常用的类型。
同样的道理,不要给System.Diagnostics.Events名字空间增加一个名为EventLog的类型,因为System.Diagnostics名字空间已经包含了一个名为EventLog的类型。
4. 技术名字空间组
这个类别包括所有那些以相同的两个前缀(<Company>.<Technology>*)开始的名字空间,比如Microsoft.Build.Utilities和Microsoft.Build.Tasks。属于同一个技术的类型彼此之间不产生冲突是很重要的。
× 不要给类型起会与位于同一技术中的其他类型产生冲突的名字。
×不要在技术名字空间和应用程序模型名字空间的类型之间引入类型名冲突(除非不打算将该技术与该应用程序模型一起使用)。
例如,不应该给Microsoft.VisualBasic名字空间增加一个名为Binding的类型,因为System.Windows.Forms名字空间已经包含该类型名。
一般来说,类型名应该是名词词组,因为它们代表系统中的实体。一条很好的经验法则是,如果无法为类型找到一个名词词组,那么可能应该重新考虑该类型的总体设计。
另一个重要的考虑因素是最易于识别的名字应该用于最常用的类型,即便从纯技术的角度来说该名字更适合其他某个不太常用的类型。例如,在一个主要场景中用来把打印作业提交到打印队列的类型应该命名为Printer,而不是PrintQueue。虽然从技术上说,该类型代表一个打印队列而不是物理设备(打印机),但是从场景的角度来说,Printer是最理想的名字,因为大多数人对提交打印作业感兴趣,对与物理打印机相关的其他操作(例如配置打印机)并不感兴趣。如果需要提供另一个类型来对应物理打印机,比如用在配置场景中,那么该类型可以被命名为PrinterConfiguration或PrinterManager。
与此相似,最常用的类型名应该反映出使用场景,而不是继承层次。大多数用户只使用继承层次的叶结点,他们几乎不会关心类层次的结构。然而,API设计者常常把继承层次作为选择类型名的最重要的标准。例如,Stream、StreamReader、TextReader、StringReader以及FileStream都相当好地描述了每个类型在继承层次中的位置,但它们模糊了对大多数用户来说最为重要的信息:为了从文件中读取文本,哪个类型才是他们需要实例化的。
下面的命名规范适用于一般的类型命名。
√ 要用名词或名词词组来给类型命名,在少数情况下也可以用形容词词组来给类型命名。在命名时要使用PascalCasing大小写风格。
这使类型名和方法区分开,后者用动词词组来命名。
× 不要给类名加前缀(例如“C”)。
√ 考虑让派生类的名字以基类的名字结尾。
这样可读性会非常好,而且能清楚地解释它们之间的关系。例子有:ArgumentOutOf- RangeException,它是一种Exception;SerializableAttribute,它是一种Attribute。但是,在运用这一条时,很重要的一点是要做出合理的判断;例如,即使Button类的名字中没有出现Control字样,它仍是一种Control事件。下面是一些正确命名的例子:
public class FileStream : Stream { … }
public class Button : Control { … }
√ 要让接口的名字以字母I开头,这样可以显示出该类型是一个接口。
例如,IComponent(描述性的名词)、ICustomAttributeProvider(名词短语)以及IPersistable(形容词)都是恰当的接口名字。同其他类型名一样,要避免使用单词缩写
√ 要确保一对类/接口的名字只相差一个“I”前缀,如果该类是该接口的标准实现。
下面以IComponent接口及其标准实现——Component类为例来说明本条规范。
public interface IComponent { … }
public class Component : IComponent { … }
泛型是.NET框架2.0版的一个主要新特性,它引入了一种称为类型参数(type parameter)的新标识符。下面的规范描述了与类型参数有关的命名约定。
√ 要用描述性的名字来命名泛型类型参数——除非一个字母就足够了,而且描述性的名字并不能增添什么价值。
public interface ISessionChannel<TSession> { … }
public delegate TOutput Converter<TInput, TOutput>(TInput from);
public class List<T> { … }
√ 考虑用T来命名参数类型——如果类型只有一个类型参数,且类型参数只有一个字母。
public int IComparer<T> { … }
public delegate bool Predicate<T>(T item);
public struct Nullable<T> where T:struct { … }
√ 要给描述性的类型参数名加上T前缀。
public interface ISessionChannel<TSession> where TSession :
ISession
{
TSession Session { get; }
}
√ 考虑在类型参数名中显示出施加于该类型参数上的限制。
例如,可以把一个被限制为ISession的类型参数命名为TSession。
如果要从.NET框架所包含的类型派生新类型,或者要实现.NET框架中的类型,那么遵循本节中的规范是非常重要的。
√ 要遵循表3-4中描述的规范——如果要从.NET框架的类型派生新类,或者要实现.NET框架中的类型。
这些后缀的规范适用于以某个特定的类型为基类的整个继承层次。例如,不仅仅是直接派生自System.Exception的类型才需要后缀,那些派生自Exception的子类型的类型也需要。
应该把这些后缀留给那些在下表中列出的类型。对那些派生自或实现其他类型的类型来说,不应该使用这些后缀。例如,下面是一些不正确的命名:
public class ElementStream : Object { … }
基 类 | 派生类型/实现类型的规范 | ||
System.Attribute | √要给自定义的attribute类添加“Attribute”后缀 | ||
System.Delegate | √要给用于事件处理的委托添加“EventHandler”后缀 √要给用于事件处理之外的那些委托添加“Callback”后缀 ×不要给委托添加“Delegate”后缀 | ||
System.EventArgs | √要添加“EventArgs”后缀 | ||
System.Enum | ×不要派生自该类;要用编程语言提供的关键字来代替。例如在C#中,要用enum关键字 ×不要添加“Enum”或“Flag”后缀 | ||
System.Exception | √要添加“Exception”后缀 | ||
System.Collections.IDictionary System.Collections.Generic. IDictionary<TKey,TValue> | √要添加“Dictionary”后缀。要注意IDictionary是一种特殊类型的集合,但本条规范优先于下面列出的更一般的集合的规范 | ||
System.Collections.IEnumerable System.Collections.ICollection System.Collections.IList System.Collections.Generic. IEnumerable<T> System.Collections.Generic. ICollection<T> System.Collections.Generic.IList<T> |
√要添加“Collection”后缀 | ||
System.IO.Stream | √要添加Stream后缀 | ||
System.Security.CodeAccessPermission System.Security.IPermission | √要添加Permission后缀 |
一般来说,枚举类型(也称为enum)的命名应该遵循标准的命名规则(PascalCasing大小写风格等等)。但是,还有另外一些针对枚举类型的具体规范。
√ 要用单数名词来命名枚举类型,除非它表示的是位域(bit field)。
public enum ConsoleColor {
Black,
Blue,
Cyan,
…
}
√ 要用复数名词来命名表示位域的枚举类型,这样的枚举类型也称为标记枚举(flag enum)。
[Flags]
public enum ConsoleModifiers {
Alt,
Control,
Shift
}
× 不要给枚举类型的名字添加“Enum”后缀。
例如,下面的命名就不好:
// Bad naming
public enum ColorEnum {
…
}
× 不要给枚举类型的名字添加“Flag”或“Flags”后缀。
例如,下面的命名就不好:
// Bad naming
[Flags]
public enum ColorFlags {
…
}
× 不要给枚举类型值的名字添加前缀(例如,给ADO枚举类型加“ad”前缀,给rich text枚举类型加“rtf”前缀)。
public enum ImageMode {
ImageModeBitmap = 0, // Image prefix is not necessary
ImageModeGrayScale = 1,
ImageModeIndexed = 2,
ImageModeRgb = 3,
}
下面的命名会更好:
public enum ImageMode {
Bitmap = 0,
GrayScale = 1,
Indexed = 2,
Rgb = 3,
}
类型由方法、属性、事件、构造函数以及字段等成员组成。下面几节描述了为类型成员命名的规范。
因为方法是用来执行操作的,因此框架设计的规范要求方法名必须是动词或动词词组。它还用来把方法同属性和类型名区分开,属性和类型名是名词或形容词词组。
√ 要用动词或动词词组来命名方法。
public class String {
public int CompareTo(…);
public string[] Split(…);
public string Trim();
}
与其他成员不同,属性应该用名词词组或形容词词组来命名。这是因为属性代表数据,因此它的名字应该反映出这一点。在命名属性时要始终使用PascalCasing大小写风格。
√ 要用名词、名词词组或形容词来命名属性。
public class String {
public int Length { get; }
}
× 不要让属性名看起来与“Get”方法的名字相似,如下面的例子所示。
public string TextWriter { get {...} set {...} }
public string GetTextWriter(int value) {...}
这种情况一般说明该属性实际上应该是一个方法,更多的信息见5.1节。
√ 要用肯定性的短语(CanSeek而不是CantSeek)来命名布尔属性。如果有帮助,还可以有选择性地给布尔属性添加“Is”、“Can”或“Has”等前缀。
例如,CanRead要比Readable更容易理解,但Created却比IsCreated的可读性更好。前缀通常是多余的,也没有必要,尤其是在有Intellisense的代码编辑器中。输入MyObject.Enabled = 与输入MyObject.IsEnabled = 一样清楚,两种情况下Intellisense都会提示你选择true或false,但后者更为冗长一些。
√ 考虑用属性的类型名来命名属性。
例如,下面这个属性的作用是取得和设置一个名为Color的枚举值,因此它被命名为Color:
public enum Color {...}
public class Control {
public Color Color { get {...} set {...} }
}
事件总是表示一些动作,要么是正在发生的,要么是已经发生的。因此,事件和方法一样,要用动词来命名,但除此之外,还要用动词的时态来表示事件发生的时间。
√ 要用动词或动词短语来命名事件。
这样的例子包括Clicked、Painting、DroppedDown等等。
√ 要用现在时和过去时[1]来赋予事件名以之前和之后的概念。
例如,在窗口关闭之前发生的close事件应该命名为Closing,而在窗口关闭之后发生的应该命名为Closed。
× 不要用“Before”或“After”前缀或后缀来区分前置事件和后置事件。
√ 要在命名事件处理函数(用作事件类型的委托)时加上“EventHandler”后缀,如下面的例子所示:
public delegate void ClickedEventHandler(object sender, ClickedEventArgs e);
√ 要在事件处理函数中用sender和e作为两个参数的名字。
参数sender表示触发该事件的对象。虽然参数sender可以是一个更为具体的类型,但一般来说其类型就是object。整个.NET框架一致地使用了这种模式。
public delegate void <EventName>EventHandler(object sender, <EventName>EventArgs e);
√ 要在命名事件的参数类时加上“EventArgs”后缀,如下面的例子所示:
public class ClickedEventArgs : EventArgs {
int x;
int y;
public ClickedEventArgs(int x, int y) {
this.x = x;
this.y = y;
}
public int X { get { return x; } }
public int Y { get { return y; } }
}
字段的命名规范适用于静态公有字段和静态受保护字段。这些规范并未涵盖内部字段和私有字段,依据第5章描述的成员设计的规范,实例的公有字段或受保护字段是不允许使用的。
√ 要在命名字段时使用PascalCasing大小写风格。
public class String {
public static readonly string Empty;
}
√ 要用名词或名词短语来命名字段。
× 不要给字段名添加前缀。
例如,不要用“g_”或“s_”来区分静态和非静态字段。
遵循参数的命名规范是很重要的,除了可读性这个显而易见的原因之外,还因为参数会在文档中显示,另外如果可视化的设计工具提供了Intellisense和类浏览功能的话,那么它们还会在设计工具中显示。
√ 要在命名参数时使用camelCasing大小写风格。
public class String {
public bool Contains(string value);
public string Remove(int startIndex, int count);
}
√ 要使用具有描述性的参数名。
参数名应该具备足够的描述性,使得在大多数情况下,用户根据参数的名字和类型就能够确定它的意思。
√ 考虑根据参数的意思而不是参数的类型来命名参数。
开发工具必须向用户提供关于类型的有用信息,这样用户就能更好地利用参数名来描述语义,而不是描述类型。偶尔使用基于类型的参数名是完全可以的,但在采用这些规范时再回到匈牙利命名法则是绝对不应该的。
本地化的资源就好比是属性,可以通过特定的对象来引用。因此,资源的命名规范与属性的命名规范相似。
√ 要在命名资源关键字(resource key)时使用PascalCasing大小写风格。
√ 要使标识符的名字具有描述性而不是使名字变短。
应该尽可能地使名字简短,但前提是不能牺牲可读性。
× 不要使用各主要CLR编程语言特有的关键字。
√ 要在命名资源时仅使用字母、数字和下划线。
√ 要用点号来给标识符清楚地划分层次。
例如,如果要设计菜单系统的资源,那么可以考虑按下面的方式来命名它们:
Menus.FileMenu.Close.Text
Menus.FileMenu.Close.Color
Menus.FileMenu.SaveAs.Text
Menus.HelpMenu.About.Text
√ 要在为异常的消息资源命名时遵循下面的命名约定。
资源标识符应该是异常的类型名加上一个简短的异常标识符,之间以点号分隔:
ArgumentException.IllegalCharacters
ArgumentException.InvalidName
ArgumentException.FileNameIsMalformed
命名规范提供了一种统一方案,如果加以遵循,框架的使用者就能轻易地识别出框架中各元素的功能。这些规范还能在不同的组织或公司开发的各框架之间提供一致的命名。
文章评论(0条评论)
登录后参与讨论