离开函数的三种方式中,抛出异常的时机是无法选择的,它关系到函数能否正常执行或者是严重的安全问题,因此一旦出现问题,必须立即抛出异常。而函数自然退出也没有什么值得讨论的意义。真正能影响代码逻辑的,则是通过return语句进行的强制返回。根据结构化程序设计的要求,函数应该分别只有一个入口和出口——这无疑是否定了在函数结尾处之外的位置使用return的权利。不过在实际开发过程中,单出口的要求并不那么实用。例如下面这个程序:
代码示例7-15:使用多出口模式的典型示例 public static PhotoFormat FromExtension(string extension) { switch (extension.ToLower()) { case ".bmp": return PhotoFormat.Bmp; case ".gif": return PhotoFormat.Gif; case ".jpg": case ".jpeg": return PhotoFormat.Jpeg; case ".png": return PhotoFormat.Png; case ".tif": case ".tiff": return PhotoFormat.Tiff; default: return PhotoFormat.Unknown; } }
这个程序完全违背了单出口的设计原则,但它仍然非常直观,逻辑清晰可见。阅读程序时,一眼可以看出这个并列关系,一个分支读完,就知道程序执行完毕,无须再阅读后面的部分。相反,如果将其改为单出口模式,我们还必须再引入一个临时的本地变量:
public static PhotoFormat FromExtension(string extension) { PhotoFormat result = null; switch (extension.ToLower()) { case ".bmp": result = PhotoFormat.Bmp; break; case ".gif": result = PhotoFormat.Gif; break; case ".jpg": case ".jpeg": result = PhotoFormat.Jpeg; break; case ".png": result = PhotoFormat.Png; break; case ".tif": case ".tiff": result = PhotoFormat.Tiff; break; default: result = PhotoFormat.Unknown; break; } return result; }
这不但增添了许多break语句,读者读完case分支之后,不得不再寻找switch之后的语句继续阅读,因为他们并不知道后面还会发生什么。代码的可读性显然不如前者。不过,如果分支本身并不能解决所有问题的话,或者并不是每个分支都可以独立完成任务时,下面的单出口模式则更为严谨:
1: //代码示例7 16: 采用单出口模式的典型示例
2: public Question AddNew(string typeCode)
3: {
4: Question newq = null;
5:
6: switch (typeCode)
7: {
8: case QuestionTypes.Text:
9: newq = new TextQuestion(ownerQuestionnaire);
10: break;
11:
12: case QuestionTypes.RadioChoices:
13: newq = new RadioChoicesQuestion(ownerQuestionnaire);
14: break;
15:
16: case QuestionTypes.CheckChoices:
17: newq = new CheckChoicesQuestion(ownerQuestionnaire);
18: break;
19:
20: case QuestionTypes.RadioTableChoices:
21: newq = new RadioTableChoicesQuestion(ownerQuestionnaire);
22: break;
23:
24: default:
25: throw new InvalidCastException("未知的问题类型标识:" + typeCode);
26: }
27:
28: Add(newq);
29: return newq;
30: }
由于switch中每个分支并不能完成所有任务,它们在给出一个中间结果后,还需要进一步的处理。在这个程序中,处理只是简单地调用Add函数,似乎直接写进case分支也不会增加太多的复杂度。但如果这个处理涉及到更多的操作时,分散在case分支内就会导致代码冗余。应该让switch结构专心产生一个中间结果,然后再让后面的代码继续工作,最后从一个出口离开函数。