C# 命名规范
前言
一致的命名模式是托管类库中可预知性与可发现性最重要的元素之一。对这些命名指南以及代码惯例的广泛的使用和理解将消除许多最常见的用户问题。 该规范都是面向基于 .NET Framework 的开发,语言是面向 C# 这门很现代的语言。 在整理过程中,主要参考了MSDN,Idesign.net 和 Philips 的代码规范,加上了一些我们自己的理解以及经验。 规范中的大部分规则都是可以用工具检测的,请各位在适应这些规范的规程中,自觉遵守这些规范。 默认情况下,文中提到的大部分规范都是必须严格遵守的。推荐的非强制性规范除外。 执行过程中,有任何不清楚的地方请联系 MontaqueHou@ozzo.com
命名规范和样式
1.大小写样式 使用下面的三种大写标识符约定。
- Pascal 大小写
将标识符的首字母和后面连接的每个单词的首字母都大写。可以对三字符或更多字符的标识符使用 Pascal 大小写。例如: BackColor
- Camel 大小写
标识符的首字母小写,而每个后面连接的单词的首字母都大写。例如: backColor
- 大写
标识符中的所有字母都大写。仅对于由两个或者更少字母组成的标识符使用该约定。例如: System.IO System.Web.UI
可能还必须大写标识符以维持与现有非托管符号方案的兼容性,在该方案中所有大写字母经常用于枚举和常数值。一般情况下,在使用它们的程序集之外这些字符应当是不可见的。 下表汇总了大写规则,并提供了不同类型的标识符的示例。 标识符 大小写 示例
类 |
Pascal |
AppDomain |
枚举类型 |
Pascal |
ErrorLevel |
枚举值 |
Pascal |
FatalError |
事件 |
Pascal |
ValueChange |
异常类 |
Pascal |
WebException 注意 总是以 Exception 后缀结尾。 |
只读的静态字段 |
Pascal |
RedValue |
接口 |
Pascal |
IDisposable 注意 总是以 I 前缀开始。 |
方法 |
Pascal |
ToString |
命名空间 |
Pascal |
System.Drawing |
参数 |
Camel |
typeName |
解释:其实规则很容易理解,一般情况下都是 Pascal 格式,除了方法的参数是使用 Camel 格式的规则。这些规则微软的类库都是严格遵守的,自己写代码的时候不知道用 Pascal 还是Camel ,只要看一下微软的智能提示就行了。比如,我想看一下静态的公共方法应该是那种格式,你可以找一个微软写好的静态方法的例子,Applicaton.Run () ,如图所示:
你可以看到Run 是一个静态的方法,它的命名是 Pascal 格式,而其方法的参数 context 是Camel 格式。
区分大小写
为了避免混淆和保证跨语言交互操作,请遵循有关区分大小写的使用的下列规则:
- 不要使用要求区分大小写的名称。对于区分大小写和不区分大小写的语言,组件都必须完全可以使用。不区分大小写的语言无法区分同一上下文中仅大小写不同的两个名称。因此,在创建的组件或类中必须避免这种情况。
- 不要创建仅是名称大小写有区别的两个命名空间。例如,不区分大小写的语言无法区分以下两个命名空间声明。
namespace ee.cummings; namespace Ee.Cummings;
- 不要创建具有仅是大小写有区别的参数名称的函数。下面的示例是不正确的。
void MyFunction(string a, string A)
- 不要创建具有仅是大小写有区别的类型名称的命名空间。在下面的示例中,Point p 和 POINT p 是不适当的类型名称,原因是它们仅在大小写方面有区别。
System.Windows.Forms.Point p System.Windows.Forms.POINT p
- 不要创建具有仅是大小写有区别的属性名称的类型。在下面的示例中,int Color 和 int COLOR 是不适当的属性名称,原因是它们仅在大小写方面有区别。
int Color {get, set} int COLOR {get, set}
- 不要创建具有仅是大小写有区别的方法名称的类型。在下面的示例中,calculate 和 Calculate 是不适当的方法名称,原因是它们仅在大小写方面有区别。
NESC C# Coding Standard
void calculate() void Calculate()
解释:通过区分大小写来区分两个不同的对象,确实不是好习惯。.NET 本身有一个公共语言规范,为了测试你的代码是否兼容,可以在 AssemblyInfo.cs 中加入
[assembly: System.CLSCompliant(true)]
这样在编译的时候,编译器会自动判断你的代码是否存在类似通过大小写来区分对象等与公共语言规范不兼容的地方。 所以在用框架警察测试你的代码的时候,一般都会告诉你,应该对你的程序集加入 CLSComplaint 特性。
3.缩写
为了避免混淆和保证跨语言交互操作,请遵循有关区缩写的使用的下列规则:
- 不要将缩写或缩略形式用作标识符名称的组成部分。例如,使用 GetWindow,而不要使用 GetWin。
- 不要使用计算机领域中未被普遍接受的缩写。
- 在适当的时候,使用众所周知的缩写替换冗长的词组名称。例如,用 UI 作为 User Interface 的缩写,用 OLAP 作为 On-line Analytical Processing 的缩写。
- 在使用缩写时,对于超过两个字符长度的缩写,请使用 Pascal 大小写或 Camel 大小写。例如,使用 HtmlButton 或 htmlButton。但是,应当大写仅有两个字符的缩写,如,System.IO,而不是 System.Io。
- 不要在标识符或参数名称中使用缩写。如果必须使用缩写,对于由多于两个字符所组成的缩写请使用 Camel 大小写,虽然这和单词的标准缩写相冲突。
解释:通过缩写的规定,你就可以理解为什么还有有 System.IO 之类的采用缩写的命名空间了。:)
4.命名空间命名指南
命名命名空间时的一般性规则是使用公司名称,后跟技术名称和可选的功能与设计,如下所示。
CompanyName.TechnologyName[.Feature][.Design]
例如:
Microsoft.Media Microsoft.Media.Design NESC.Data.SQLHelper
给命名空间名称加上公司名称或者其他知名商标的前缀可以避免两个已经发布的命名空间名称相同的可能性。例如,Microsoft.Office 是由 Microsoft 提供的 Office Automation Classes 的一个适当的前缀。
在第二级分层名称上使用稳定的、公认的技术名称。将组织层次架构用作命名空间层次架构的基础。命名一个命名空间,该命名空间包含为具有 .Design 后缀的基命名空间提供设计时功能的类型。例如,System.Windows.Forms.Design 命名空间包含用于设计基于 System.Windows.Forms 的应用程序的设计器和相关的类。
嵌套的命名空间应当在包含它的命名空间中的类型上有依赖项。例如,System.Web.UI.Design 中的类依赖于 System.Web.UI 中的类。但是,System.Web.UI 中的类不依赖于 System.Web.UI.Design 中的类。
应当对命名空间使用 Pascal 大小写,并用句点分隔逻辑组件,如 Microsoft.Office.PowerPoint 中所示。如果您的商标使用非传统的大小写,请遵循您的商标所定义的大小写,即使它与规定的 Pascal 大小写相背离。例如,命名空间 NeXT.WebObjects 和 ee.cummings 阐释了对于 Pascal 大小写规则的适当背离。
如果在语义上适当,使用复数命名空间名称。例如,使用 System.Collections 而不是 System.Collection。此规则的例外是商标名称和缩写。例如,使用 System.IO 而不是 System.IOs。
不要为命名空间和类使用相同的名称。例如,不要既提供 Debug 命名空间也提供 Debug 类。 最后,请注意命名空间名称不必非得与程序集名称相似。例如,如果命名程序集 MyCompany.MyTechnology.dll,它没有必要非得包含 MyCompany.MyTechnology 命名空间。对于这一点,我们还是希望两者相同。这样可以避免文件重名。
5.类命名指南
以下规则概述命名类的指南:
- 使用名词或名词短语命名类。
- 使用 Pascal 大小写。
- 少用缩写。
- 不要使用类型前缀,如在类名称上对类使用 C 前缀。例如,使用类名称 FileStream,而不是 CFileStream。
- 不要使用下划线字符 (_)。
- 有时候需要提供以字母 I 开始的类名称,虽然该类不是接口。只要 I 是作为类名称组成部分的整个单词的第一个字母,这便是适当的。例如,类名称 IdentityStore 就是适当的。
- 在适当的地方,使用复合单词命名派生的类。派生类名称的第二个部分应当是基类的名称。例如,ApplicationException 对于从名为 Exception 的类派生的类是适当的名称,原因是 ApplicationException 是一种 Exception。请在应用该规则时进行合理的判断。例如,Button 对于从 Control 派生的类是适当的名称。尽管按钮是一种控件,但是将 Control 作为类名称的一部分将使名称不必要地加长。 如果我么写一个xp格式的按钮,从button继承的话,可以定义为ImageButton
下面是正确命名的类的示例。
public class FileStream public class Button public class String
6.接口命名指南
以下规则概述接口的命名指南:
- 用名词或名词短语,或者描述行为的形容词命名接口。例如,接口名称 IComponent 使用描述性名词。接口名称 ICustomAttributeProvider 使用名词短语。名称 IPersistable 使用形容词。
- 使用 Pascal 大小写。
- 少用缩写。
- 给接口名称加上字母 I 前缀,以指示该类型为接口。
- 在定义类/接口对(其中类是接口的标准实现)时使用相似的名称。两个名称的区别应该只是接口名称上有字母 I 前缀。 如果 IDBCommand, SQLCommand ,不要改为IDBCommand, ExecuteSQLCommand。
- 不要使用下划线字符 (_)。
以下是正确命名的接口的示例。
public interface IServiceProvider public interface IFormatable
以下代码示例阐释如何定义 IComponent 接口及其标准实现 Component 类。
public interface IComponent { // Implementation code goes here. } public class Component: IComponent { //
7.属性(Attribute ,不是 Property)命名指南
应该总是将后缀 Attribute 添加到自定义属性类。以下是正确命名的属性类的示例。 public class ObsoleteAttribute{}
8.枚举类型命名指南
枚举 (Enum) 值类型从 Enum 类继承。以下规则概述枚举的命名指南: NESC C# Coding Standard
- 对于 Enum 类型和值名称使用 Pascal 大小写。 Color.Red
- 少用缩写。
- 不要在 Enum 类型名称上使用 Enum 后缀。 比如:ColorEnum
- 对大多数 Enum 类型使用单数名称,但是对作为位域的 Enum 类型使用复数名称。因为 BIT 的枚举经常做叠加或者 OR 运算,比如
[Flags] public enum WindowStyles { Title, //有title Border //有边框 } WindowStyles windowStyle=WindowStyles.Border | WindowStyles.Title ;
- 总是将 FlagsAttribute 添加到位域 Enum 类型。
9.参数命名指南
必须仔细遵守这些参数的命名指南,这非常重要,因为提供上下文相关帮助和类浏览功能的可视化设计工具会在设计器中对用户显示方法参数名称。以下规则概述参数的命名指南:
- 对参数名称使用 Camel 大小写。
- 使用描述性参数名称。参数名称应当具有足够的描述性,以便参数的名称及其类型可用于在大多数情况下确定它的含义。例如,提供上下文相关帮助的可视化设计工具会按开发人员键入的实际内容显示方法参数。在这种情况下,方法参数名称的表述必须清楚明白,开发人员才能提供正确的参数。
- 使用描述参数的含义的名称,而不要使用描述参数的类型的名称。开发工具将提供有关参数的类型的有意义的信息。因此,通过描述意义,可以更好地使用参数的名称。少用基于类型的参数名称,仅在适合使用它们的地方使用它们。
- 不要使用保留的参数。保留的参数是专用参数,如果需要,可以在未来的版本中公开它们。相反,如果在类库的未来版本中需要更多的数据,请为方法添加新的重载。
- 不要给参数名称加匈牙利语类型表示法的前缀。
以下是正确命名的参数的示例。
Type GetType(string typeName) string Format(string format, object[] args) OrderStatus GetOrderState(string orderNumber)
10. 方法命名指南
以下规则概述方法的命名指南:
- 使用动词或动词短语命名方法。
- 使用 Pascal 大小写。
以下是正确命名的方法的实例。
RemoveAll() GetCharArray() Invoke()
11.属性命名指南
以下规则概述属性的命名指南:
- 使用名词或名词短语命名属性。
- 使用 Pascal 大小写。
- 不要使用匈牙利语表示法。
- 考虑用与属性的基础类型相同的名称创建属性。例如,如果声明名为 Color 的属性,则属性的类型同样应该是 Color。请参见本主题内下文中的示例。
以下代码示例阐释正确的属性命名。
public class SampleClass { public Color BackColor { // Code for Get and Set accessors goes here. } }
以下代码示例阐释提供其名称与类型相同的属性。
public enum Color { // Insert code for Enum here. } public class Control { public Color Color { get {// Insert code here.} set {// Insert code here.} } }
以下代码示例不正确,原因是 Color 属性是 Integer 类型的。
public enum Color {// Insert code for Enum here.} public class Control { public int Color { get {// Insert code here.} set {// Insert code here.} } }
在不正确的示例中,不可能引用 Color 枚举的成员。Color.Xxx 将被解释为访问一个成员,该成员首先获取 Color 属性(在 Visual Basic 中为 Integer 类型,在 C# 中为 int 类型)的值,然后再访问该值的某个成员(该成员必须是 System.Int32 的实例成员)。
12.事件命名指南
以下规则概述事件的命名指南:
- 使用 Pascal 大小写。
- 不要使用匈牙利语表示法。
- 对事件处理程序名称使用 EventHandler 后缀。
- 指定两个名为 sender 和 e 的参数。sender 参数表示引发事件的对象。sender 参数始终是 object 类型的,即使在可以使用更为特定的类型时也如此。与事件相关联的状态封装在名为 e 的事件类的实例中。对 e 参数类型使用适当而特定的事件类。
- 用 EventArgs 后缀命名事件参数类。
- 考虑用动词命名事件。例如,命名正确的事件名称包括 Clicked、Painting 和 DroppedDown。
- 使用动名词(动词的“ing”形式)创建表示事件前的概念的事件名称,用过去式表示事件后。例如,可以取消的 Close 事件应当具有 Closing 事件和 Closed 事件。不要使用 BeforeXxx/AfterXxx 命名模式。
- 不要在类型的事件声明上使用前缀或者后缀。例如,使用 Close,而不要使用 OnClose。
- 通常情况下,对于可以在派生类中重写的事件,应在类型上提供一个受保护的方法(称为 OnXxx)。此方法只应具有事件参数 e,因为发送方总是类型的实例。
以下示例阐释具有适当名称和参数的事件处理程序。
public delegate void MouseEventHandler(object sender, MouseEventArgs e);
以下示例阐释正确命名的事件参数类。
public class MouseEventArgs : EventArgs { int x; int y; public MouseEventArgs(int x, int y) { this.x = x; this.y = y; } public int X { get { return x; } } public int Y { get { return y; } } }
13.C# 预定义类型使用指南
公司统一规定使用内置的预定义类型。比如 int 而不是 System.Int32 或者 Int32 string 而不是 System.String object 而不是Object 使用预定义类型的优点:
- VS.NET IDE 会highlight 这些关键字。
- 统一的变成风格。比如 所有 object 的地方都是 object,而不是既有Object,又有System.Object
- 跟vs.net IDE 自动生成的代码以及微软的示例代码保持一致的风格。
为了方便C 和 C++ 程序员容易使用 C#, C# 提供了预定义的类型。这些类型在编译的时候都回被替换为.NET 框架的数据类型。比如 int 会替换为 System.Int32. 以下是一张对应表。
C# 类型 |
.NET Framework 类型 |
bool |
System.Boolean |
byte |
System.Byte |
sbyte |
System.SByte |
char |
System.Char |
decimal |
System.Decimal |
double |
System.Double |
float |
System.Single |
int |
System.Int32 |
uint |
System.UInt32 |
long |
System.Int64 |
ulong |
System.UInt64 |
object |
System.Object |
short |
System.Int16 |
ushort |
System.UInt16 |
string |
System.String |
14.避免在命名空间内部使用 using namespace
比如以下是不可取的
namespace NESC.TR.DemoSpec { using System; //不要嵌套在Namespace 内部使用 using using System.IO; class HelloWorld { }
}
15.将所有的框架命名空间分组,将用户或第三方命名空间放置在其下
采用类似的格式:
using System; using System.Collections; using System.ComponentModel; using System.Data;
空行隔开
using Microsoft; using MyCompany; using MyControls;
解释:.NET 程序理论上有很好的可移植特性,希望大家在写代码的时候刻意的考虑这一特性。比如命名空间分开,就很容易看出来哪些namespace 可能是平台相关的。就.NET 的类库而言,System 打头的一般来说都是平台无关的。而类似像注册表这类特性跟平台相关的,微软一般都放在 Microsoft namespace 下面。这个特性也可以让你容易猜出来类似注册表,C# 代码生成等平台相关的类放在 Microsoft 下面。水晶报表肯定在CrystalDecisions 下面。
16.关于程序注释:
程序的注释是程序可读性的一个重要参考,请注意一下几点:
- 将注释缩保持在与代码缩进同等级的位置上。
- 英文的注释应该通过拼写检查,不要有拼写错误的单词。
- 每个文件的开头尽可能的说明该文件的用途,作者,创建时间,版本等。采用单行注释/**/的方式。
- 对于所有public 类型的类,接口,枚举,结构等。都应当有C# 标准的///Summary格式的注释。
说明:对于功能性的注释,这一点有一个基本的原则就是说明做了什么,而不是怎么做。大家可以参考微软的文档,例如 Form 类有一个Doc 属性。他的注释很规范: 获取或设置控件停靠到父容器的哪一个边缘。 这里有几个可以参考:
- 对于属性应该加上获取或设置,别人一看就知道该属性是不是readonly;
- 控件停靠到父容器的哪一个边缘,而不是说怎么去停靠,或者有什么样的行为。
17.成员变量,局部变量,静态的变量使用前缀区分
对于private 的变量,使用以下约定。 1. 成员变量应该在开始就被声明,并集中放在class 的开始位置。 2. 请使用m,s 前缀来区分成员变量和静态成员变量。对于局部变量不适用任何前缀。例如:
using System; namespace NESC.CSharp { class VarUsing { private static bool s_isOK=false; private bool m_isOK=false; public void Test() { bool is_OK=false; } }
}