好程序员的标记之一是他们编写可读的代码。你的职业生涯不会总是在高山之巅独自编程,所以还需要让你的同伴能够使用和修改你的程序。下面的编码指导和惯例将有助于你写出清晰、可读的C#程序。
1.15.1 恰当地使用缩进
让C#程序可读的坦途之一就是使用缩进清楚地描述语句间层次关系。代码块中的语句应相对于包围该块的开始/结束行做缩进(如相对于带有大括号的行做缩进)。MSDN网页上的例子用四个空格,但有些程序员只用两个空格,有些则用三个。本书的例子都用两个空格作为缩进约定。
要想知道缩进怎样让程序变得可读,请考虑下面两个程序。第一个程序不做缩进:
using System;
public class StyleDemo
{
static void Main() {
string name = "cheryl";
for (int i = 0; i < 4; i++) {
if (i != 2) {
Console.WriteLine(name + " " + i);
}
}
Console.WriteLine("下一个是什么");
}
}
人家得费多大功夫,才能通读这段程序和了解它的功能何在;这不是可读的代码。
现在来看看做了适当缩进的同一个程序。代码块内每个语句都相对包围块做了两个空格的缩进。这下就很容易知道代码在干什么了。例如,if语句在for语句代码块内,这很清楚。最后一个WriteLine方法调用位于for循环以外,这也很明白。两个版本的程序执行时得到同样的结果,但第二个版本却更具可读性。
using System;
public class StyleDemo
{
static void Main() {
string name = "Cheryl";
for (int i = 0; i < 4; i++) {
if (i != 2) {
Console.WriteLine(name + " " + i);
}
}
Console.WriteLine("下一个是什么");
}
}
代码输出结果如下:
Cheryl 0
Cheryl 1
Cheryl 2
What’s next
不做恰当缩进将导致程序难以卒读,从而难以调试——假若出现因括号不对称导致的编译器错误,这种错误信息往往在问题所在后面很远处才出现。例如,下面程序的第12行缺少一个起始大括号,但直至第26行编译器才报告错误!
using System;
public class Indent2
{
static void Main() {
int x = 2;
int y = 3;
int z = 1;
if (x >= 0) {
if (y > x) {
if (y > 2) // 第12行缺少起始大括号,但是……
Console.WriteLine("A");
z = x + y;
}
else {
Console.WriteLine("B");
z = x - y;
}
}
else {
Console.WriteLine("C");
z = y - x;
}
}
else Console.WriteLine("D"); // 编译器到第26行才报错!
}
}
编译器显示的错误信息不可理喻;它指出第26行出错:
IndentDemo.cs (26,5) error CS1519: Invalid token.
这根本无助于我们找到位于第12行的错误。然而,如果我们已经做了恰当缩进,就比较容易找到缺失的大括号。
有时,有太多层嵌套缩进,或单行语句过长,在编辑器查看或打印出来时就会出现折行:
while (a < b) {
while (c > d) {
for (int j = 0; j < 29; j++) {
x = y + z + a + b - 125
(c * (d / e) + f) - g + h + j - l - m - n + o +
p * q / r + s;
}
}
}
要避免这种状况,最好是在空格或标点符号处折行:
while (a < b) {
while (c > d) {
for (int j = 0; j < 29; j++) {
// 推荐这样分行。
x = y + z + a + b - 125(c * (d / e) + f) - g +
h + j - l - m - n + o + p * q / r + s;
}
}
}
1.15.2 聪明地使用注释
另一个使代码可读的要点是多用有意义的注释。写代码时千万要记得,你明白自己在干什么,其他人却不见得都明白。(有时我们自己还得想半天才搞得懂前段时间自己写的代码!)
如果某段代码可能引起疑惑,添加注释。
l 在注释中包含足够细节,清楚地阐述你的意图。
l 确保注释有价值,不要画蛇添足。下面的注释百无一用,纯属多此一举:
// 声明x为整数,赋其初始值为3。
int x = 3;
l 把代码缩进到它关联代码块的缩进层级。
为了说明让代码可读多么重要,我们来回顾一下本章稍前部分的例子:
using System;
public class IfDemo
{
static void Main() {
double sales = 40000.0;
int lengthOfService = 12;
double bonus;
if (sales > 30000.0 && lengthOfService >= 10) {
bonus = 2000.0;
}
else {
if (sales > 20000.0) {
bonus = 1000.0;
}
else {
bonus = 0.0;
}
}
Console.WriteLine("Bonus = " + bonus);
}
}
因为缺少注释,读代码者可能会难以理解程序要实现的业务逻辑。给程序添加清楚、描述性的注释后,再来看看:
using System;
// 程序计算员工奖金。
// Jacquie Barker和Grant Palmer编写于2004年1月5日。
public class IfDemo
{
static void Main() {
// 月度销售额。
double sales = 40000.0;
// 在职月份
int lengthOfService = 12;
// 奖金总额
double bonus;
// 如果(a)员工在职时间超过12个月,并且(b)本季度
// 销售额超过3万美元,则奖励两千美元。
if (sales > 30000.0 && lengthOfService >= 10) {
bonus = 2000.0;
}
else {
// 本季度销售额超过两万美元的员工
// 无论在职时间多久,均奖励1千美元。
if (sales > 20000.0) {
bonus = 1000.0;
}
// 销售额在两万美元以下的没有奖金。
else {
bonus = 0.0;
}
}
Console.WriteLine("奖金额为" + bonus);
}
}
程序现在变得更加可读,因为添加的注释解释了其中每块代码要完成的任务,或者说,解释了应用程序的业务逻辑。
1.15.3 括号的放置
对于用大括号{…}来表示代码块起始/结束的块结构语言(例如,C、C++、Java和C#),有两种放置代码块左/起始括号的编码风格。
第一种是把左括号放在开始一个块的代码行末尾,而右/结束括号则自占一行。
左括号和类声明语句同一行:
public class Test {
方法头也是如此:
static void Main() {
流程控制语句同样如法炮制:
for (int i = 0; i < 3; i++) {
Console.WriteLine(i);
每个结束括号自占一行:
}
}
}
另一种括号放置风格是每个起始括号自占一行:
public class Test
{
static void Main()
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine("i");
}
}
}
C#习惯上混用这两种方式:声明类时用第二种风格(括号独占一行),其他场合用第一种风格(括号与起始语句同行):
public class Test
{
static void Main() {
for (int i = 0; i < 3; i++) {
Console.WriteLine(i);
}
}
}
风格无所谓对错,编译器不会厚此薄彼。在代码中保持一致是好的做法,所以,请选择一种括号放置风格,并且坚持它。
无论遵循何种风格,应让结束括号和代码块首行保持同样缩进。
1.15.4 自说明的变量名
使用缩进、注释,再加上选择能自我说明的变量名,就有可能编写可读的代码。除了循环控制变量外,避免用单个字母做变量名。尽量少用缩写,除非是常用且为开发者所熟知的缩写。考虑以下变量声明:
int grd;
变量名“grd”表示什么并不十分清楚。该变量代表grid、grade还是gourd呢?更好的做法是拼出整个单词:
int grade;
另一个极端是过长的名称,如
double averageThirdQuarterReturnOnInvestment;
这也让人难以禁受。减少变量名长度的同时保持其描述性的确是个挑战,但压缩长度自有其益处。
在本书后面部分讨论方法、类等OO构造块时,我们还会谈到它们的命名约定问题。
.NET Framework提供一系列命名指南,提倡统一的C#程序风格。如果你愿意阅读完整的C#命名惯例,可以在下面的地址找到这些指南:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconnamingguidelines.asp





