前言
控制流是语言中最基础的部分,我们不谈具体的细节,只讲讲一些关键和有趣的点。
01
流控制
条件语句:if, else if, else
if语句的使用非常值得细讲,如何是好的使用习惯。有一点非常重要,就是尽可能的避免太多层次的嵌套,这将带来可读性灾难。我自己的原则是,if嵌套一般不超过2层,最多不超过3层。可读性是什么?是指:不只是你自己写的代码你自己能读懂,更重要的是当别人接手你的代码的时候,别人能读懂。
If嵌套导致的可读性灾难在Java程序里非常常见,我看别人的遗留代码就是这个感受。一个原因是Java没有像C#这样的linq工具,以及一些语法糖,导致很多常用的计算也要自己写if语句实现,这些代码再套在业务代码中,if层次就很多了,还有比如字段定义都需要生成getter和setter方法(我的Java功力太浅薄,看到大家写Java代码都是这么写,但却不能理解为什么一定要遵循这个范儿),取数判断都是getXXX(),导致布尔表达式代码看着一长串,代码太长又导致很多Java程序员会把一个if拆开成多个嵌套的if来写。比如这个if层次达到了4层(这还不算多,我见过七八层的都有,但一时找不到更多嵌套的例子):
If嵌套层次多会导致什么问题?就是多执行路径的灾难,一个最简单的if else,如果达到3层嵌套,总的可能执行路径就是2*2*2=8。你的程序要保证这8种执行可能性都被考虑到,这导致的结果往往是程序员放弃遵循严谨的逻辑,只考虑正常的业务逻辑,忽略异常的业务逻辑处理。还有些程序员编程习惯不好,只关注if 不关注else,这种程序员的代码里,大量的if语句没有else,往往就隐藏着很多的逻辑地雷。
对于if else的使用,我的使用习惯是:
- 写下if时就要想到else怎么处理,程序员要打造的是一个“执行容器”,控制容器边界是他的基本职责。只完成需求而不关注“容器”边界安全的程序员不是合格的程序员。
- 优先处理else,就是先处理异常的逻辑,并return,从而将 if else的写法替换为if return,从而使得if else 嵌套层级-1。一段代码中,先后的多个if语句都使用这种方式,可使嵌套层级持续减少,可能原本需要五六层级的if嵌套,最后只需要一级if语句实现,代码健壮性和可读性大大增强。
- 如果某些地方只需要写if不需要else,但业务逻辑比较复杂,不是能很直观的理解时,我会写空else,并在else中加注释,说明为什么else为空。
条件语句:switch
有些人不喜欢使用switch,因为if else也能完成switch的功能,因此懒得去分辨该怎么用switch。我对语言(以及框架)的使用原则不是最简单的够用就好,而是为每种语言特性找到适合它的使用场景,并坚持按最佳的使用方式编写代码。Switch的优点是什么?应用于“值相等”的比较,它格式更清晰,写法更简单,可读性更好。
Switch语法还有一种特殊用法是,多个空case语句以使多个值条件下执行同一段代码:
switch(str)
{
case "0":
case "1":
break;
default:
break;
}
循环语句:for,while,do…while,foreach
用的最多的,应该是foreach了,因为循环一个List是当今编写C#代码中最常用的操作了。当你想使用集合的行号时,foreach就不够用了,要用for语句了。非List的循环则会用while,而do…while我很少用,不是它不好用,而是while用习惯了,往往想不起去用do…while。这其实也是在提醒我们:为语言(和类库)特性找到适合它的可明确表述的使用场景是很重要的,因为只有这个意识植入了你心底里,才能在编程中自如的使用最合适的语言特性和类库工具,而不是“都行”,如果遵循的是“都行”的态度,逐渐的你会偏向只使用一种最简单的特性和工具。
循环还有几个小知识点:
For,foreach中的变量可以使用类型推断var,比如:
foreach(var user in list)
for(var i=0; i< 100; i++)
for循环可初始化多个值,比如:
for (int i = 0, j = 0; i < 100; i++, j = j + 2)
foreach循环集合时,不能更改集合项的值。如果要修改,可以使用for循环代替。但如果要在循环中删除集合的某项,for循环也不行了,这个时候可以将集合进行“变身”,使得循环的“集合”和要处理的“集合”成为两个不同的集合,是个技巧,比如:
跳转语句:goto
C#中我从来没用过goto。可以说c#中使用goto几乎没有什么优点,唯一的一个明显用途,是跳出双层循环(后面有例子),goto使得跳出双层循环非常方便,否则我们得自己使用一个独立变量来控制跳出双层循环。
跳转语句:break
Break用于退出switch,以及退出for,foreach,while,do…while的循环。
跳转语句:continue
Continue用于退出for,foreach,while,do…while的当前迭代。
跳转语句:return
Return用于退出类的方法。
我们对比.Net和Java的语法,发现在goto、break、continue上是有区别的。Java没有goto,而使用了break和continue的功能加强来替代goto的功能。而C#保留了goto,且不提供Java中break和continue中加强的功能。
.Net:
Java:
哪种方式更好呢?我更喜欢.Net的处理方式。.Net从实用主义出发,更直观,但可能有些程序员会过度使用goto。而Java表达了语言开发者对Goto的完美厌恶和彻底禁绝,同时,对continue和break的扩展思路非常精妙(但break label稍稍有点别扭)。
本来还想讲枚举,结果发现流控制也讲了不少,那么枚举到下一篇再讲。
枚举概念很简单,但我觉得有必要讨论的是枚举它简单的语言定义,和它在实际应用时存在的鸿沟,应该如何填平的问题。
觉得文章有意义的话,请动动手指,分享给朋友一起来共同学习进步。
欢迎关注本人微信公众号,更及时的关注最新文章(每周三篇原创文章,以及多篇专题文章):
附文: