2.11 C#编程准则
本节介绍编写C#程序时应该牢记和遵循的准则。大多数C#开发人员都遵守这些规则,所以在这些规则的指导下编写程序,可以方便其他开发人员使用程序的代码。
2.11.1 关于标识符的规则
本小节将讨论可用的变量、类和方法等的命名规则。注意本节所介绍的规则不仅是准则,也是C#编译器强制使用的。
标识符是给变量、用户定义的类型(如类和结构)和这些类型的成员指定的名称。标识符区分大小写,所以interestRate和InterestRate是不同的变量。确定在C#中可以使用什么标识符有两条规则:
● 尽管可以包含数字字符,但它们必须以字母或下划线开头。
● 不能把C#关键字用作标识符。
C#包含如表2-9所示的保留关键字。
表2-9
如果需要把某一保留字用作标识符(例如,访问一个用另一种语言编写的类),那么可以在标识符的前面加上前缀符号@,告知编译器其后的内容是一个标识符,而不是C#关键字(所以abstract不是有效的标识符,@abstract才是)。
最后,标识符也可以包含Unicode字符,用语法\uXXXX来指定,其中XXXX是Unicode字符的4位十六进制编码。下面是有效标识符的一些例子:
● Name
● überfluß
● _Identifier
● \u005fIdentifier
最后两个标识符完全相同,可以互换(因为005f是下划线字符的Unicode代码),所以这些标识符在同一个作用域内不要声明两次。注意,虽然从语法上看,在标识符中可以使用下划线字符,但大多数情况下最好不要这么做,因为它不符合微软公司的变量命名规则,这种命名规则可以确保开发人员使用相同的命名约定,易于阅读他人编写的代码。
注意:为什么C#最新版本添加的一些新关键字没有列在保留字列表中?原因是,如果它们添加到保留字列表中,就会破坏利用新C#关键字的现有代码。解决方案是把这些关键字定义为上下文关键字,以改进语法;它们只能用在某些具体的代码中。例如,async关键字只能用于方法声明,也可以用作变量名。编译器不会因此出现冲突。
2.11.2 用法约定
在任何开发语言中,通常有一些传统的编程风格。这些风格不是语言自身的一部分,而是约定,例如,变量如何命名,类、方法或函数如何使用等。如果使用某语言的大多数开发人员都遵循相同的约定,不同的开发人员就很容易理解彼此的代码,这一般有助于程序的维护。约定主要取决于语言和环境。例如,在Windows平台上编程的C++开发人员一般使用前缀psz或lpsz表示字符串:char*pszResult; char *lpszMessage;,但在UNIX系统上,则不使用任何前缀:char*Result; char *Message;。
从本书中的示例代码中可以总结出,C#中的约定是命名变量时不使用任何前缀:string Result;string Message;。
注意:变量名用带有前缀字母的方法来表示某种数据类型,这种约定称为Hungarian表示法。这样,其他阅读该代码的开发人员就可以立即从变量名中了解它代表什么数据类型。在有了智能编辑器和IntelliSense之后,人们普遍认为Hungarian表示法是多余的。
在许多语言中,用法约定是随着语言的使用逐渐演变而来的,但是对于C#和整个.NET Framework,微软公司编写了非常多的用法准则,详见.NET/C# MSDN文档。这说明,从一开始,.NET程序就有非常高的互操作性,开发人员可以以此来理解代码。用法准则还得益于20年来面向对象编程的发展,因此相关的新闻组已经仔细考虑了这些用法规则,而且已经为开发团体所接受。所以我们应遵守这些准则。
但要注意,这些准则与语言规范不同。用户应尽可能遵循这些准则。但如果有很好的理由不遵循它们,也不会有什么问题。例如,不遵循这些准则,也不会出现编译错误。一般情况下,如果不遵循用法准则,就必须有充分的理由。准则应是正确的决策,而不是一种束缚。如果比较本书的后续示例,应注意到许多示例都没有遵循该约定。这通常是因为某些准则适用于大型程序,而不适合用于小示例。如果编写一个完整的软件包,就应遵循这些准则,但它们并不适合于只有20行代码的独立程序。在许多情况下,遵循约定会使这些示例难以理解。
编程风格的准则非常多。这里只介绍一些比较重要的,以及最适合于用户的准则。如果用户要让代码完全遵循用法准则,就需要参考MSDN文档。
1.命名约定
使程序易于理解的一个重要方面是给对象选择命名的方式,包括变量、方法、类、枚举和名称空间的命名方式。
显然,这些名称应反映对象的目的,且不与其他名称冲突。在.NET Framework中,一般规则也是变量名要反映变量实例的目的,而不反映数据类型。例如,height就是一个比较好的变量名,而integerValue就不太好。但是,这种规则是一种理想状态,很难达到。在处理控件时,大多数情况下使用confirmationDialog和chooseEmployeeListBox等变量名比较好,这些变量名说明了变量的数据类型。
名称的约定包括以下几个方面。
(1) 名称的大小写
在许多情况下,名称都应使用Pascal大小写形式。Pascal大小写形式指名称中单词的首字母大写,如EmployeeSalary、ConfirmationDialog、PlainTextEncoding。注意,名称空间和类,以及基类中的成员等的名称都应遵循Pascal大小写规则,最好不要使用带有下划线字符的单词,即名称不应是employee_salary。其他语言中常量的名称常常全部大写,但在C#中最好不要这样,因为这种名称很难阅读,而应全部使用Pascal大小写形式的命名约定:
const int MaximumLength;
还推荐使用另一种大小写模式:camel大小写形式。这种形式类似于Pascal大小写形式,但名称中第一个单词的首字母不大写,如employeeSalary、confirmationDialog、plainTextEncoding。有3种情况可以使用camel大小写形式:
● 类型中所有私有成员字段的名称:
private int subscriberId;
但要注意成员字段的前缀名常常用一条下划线开头:
private int _subscriberId;
● 传递给方法的所有参数的名称:
public void RecordSale(string salesmanName, int quantity);
● 用于区分同名的两个对象——比较常见的是属性封装字段:
private string employeeName; public string EmployeeName { get { return employeeName; } }
如果这么做,则私有成员总是使用camel大小写形式,而公有的或受保护的成员总是使用Pascal大小写形式,这样使用这段代码的其他类就只能使用Pascal大小写形式的名称了(除了参数名以外)。
还要注意大小写问题。C#区分大小写,所以在C#中,仅大小写不同的名称在语法上是正确的,如上面的例子所示。但是,有时可能从Visual Basic应用程序中调用程序集,而Visual Basic不区分大小写,如果使用仅大小写不同的名称,就必须使这两个名称不能在程序集的外部访问(上例是可行的,因为仅私有变量使用了camel大小写形式的名称)。否则,Visual Basic中的其他代码就不能正确使用这个程序集。
(2) 名称的风格
名称的风格应保持一致。例如,如果类中的一个方法名为ShowConfirmationDialog(),另一个方法就不能被命名为ShowDialogWarning()或WarningDialogShow(),而应是ShowWarningDialog()。
(3) 名称空间的名称
名称空间的名称非常重要,一定要仔细考虑,以避免一个名称空间的名称与其他名称空间同名。记住,名称空间的名称是.NET区分共享程序集中对象名的唯一方式。如果一个软件包的名称空间使用的名称与另一个软件包相同,而这两个软件包都由同一个程序使用,就会出问题。因此,最好用自己的公司名创建顶级的名称空间,再嵌套技术范围较窄、用户所在小组或部门或者类所在软件包的名称空间。Microsoft建议使用如下的名称空间:<CompanyName>.<TechnologyName>,例如:
WeaponsOfDestructionCorp.RayGunControllers WeaponsOfDestructionCorp.Viruses
(4) 名称和关键字
名称不应与任何关键字冲突,这非常重要。实际上,如果在代码中,试图给某一项指定与C#关键字同名的名称,就会出现语法错误,因为编译器会假定该名称表示一条语句。但是,由于类可能由其他语言编写的代码访问,所以不能使用其他.NET语言中的关键字作为对应的名称。一般来说,C++关键字类似于C#关键字,不太可能与C++混淆,只有Visual C++常用的关键字以两个下划线字符开头。与C#一样,C++关键字都是小写字母,如果要遵循公有类和成员使用Pascal风格名称的约定,则在它们的名称中至少有一个字母大写,因此不会与C++关键字冲突。另一方面,Visual Basic的问题会多一些,因为Visual Basic的关键字要比C#的多,而且它不区分大小写,不能依赖于Pascal风格的名称来区分类和成员。
查看MSDN文档:http://msdn.microsoft.com/library。在Development Tools and Languages, C# reference中,有一个很长的C#关键字列表,不应该用于类和成员。如果可以使用Visual Basic语言访问类,还要检查Visual Basic关键字的列表。
2.属性和方法的使用
类中出现混乱的一个方面是某个特定数量是用属性还是方法来表示。这没有硬性规定,但一般情况下,如果该对象的外观像变量,就应使用属性来表示它(属性详见第3章),即:
● 客户端代码应能读取它的值。最好不要使用只写属性,例如,应使用SetPassword()方法,而不是Password只写属性。
● 读取该值不应花太长的时间。实际上,如果是属性,通常表明读取过程花的时间相对较短。
● 读取该值不应有任何明显的和不希望的负面效应。进一步说,设置属性的值,不应有与该属性不直接相关的负面效应。设置对话框的宽度会改变该对话框在屏幕上的外观,这是可以的,因为它与该属性相关。
● 可以按照任何顺序设置属性。尤其在设置属性时,最好不要因为还没有设置另一个相关的属性而抛出异常。例如,如果为了使用访问数据库的类,需要设置ConnectionString、UserName和Password,应确保已经实现了该类,这样用户才能按照任何顺序设置它们。
● 顺序读取属性应有相同的结果。如果属性的值可能会出现预料不到的改变,就应把它编写为一个方法。在监控汽车运动的类中,把speed设置为属性就不合适,而应使用GetSpeed()方法;另一方面,应把Weight和EngineSize设置为属性,因为对于给定的对象,它们是不变的。
如果要编码的相关项满足上述所有条件,就把它设置为属性,否则就应使用方法。
3.字段的使用
字段的用法非常简单。字段应总是私有的,但在某些情况下也可以把常量或只读字段设置为公有。原因是如果把字段设置为公有,就不利于在以后扩展或修改类。
遵循上面的准则就可以培养良好的编程习惯,而且这些准则应与面向对象的编程风格一起使用。
最后要记住以下有用的备注:微软公司在保持一致性方面相当谨慎,在编写.NET基类时遵循了它自己的准则。在编写.NET代码时应很好地遵循这些规则,对于基类来说,就是要弄清楚类、成员、名称空间的命名方式,以及类层次结构的工作方式等。类与基类之间的一致性有助于提高可读性和可维护性。