1. 程式人生 > >解讀經典《C#高階程式設計》第七版 Page38-45.核心C#.Chapter2

解讀經典《C#高階程式設計》第七版 Page38-45.核心C#.Chapter2

前言

 

控制流是語言中最基礎的部分,我們不談具體的細節,只講講一些關鍵和有趣的點。

 

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的使用,我的使用習慣是:

  1. 寫下if時就要想到else怎麼處理,程式設計師要打造的是一個“執行容器”,控制容器邊界是他的基本職責。只完成需求而不關注“容器”邊界安全的程式設計師不是合格的程式設計師。
  2. 優先處理else,就是先處理異常的邏輯,並return,從而將 if else的寫法替換為if return,從而使得if else 巢狀層級-1。一段程式碼中,先後的多個if語句都使用這種方式,可使巢狀層級持續減少,可能原本需要五六層級的if巢狀,最後只需要一級if語句實現,程式碼健壯性和可讀性大大增強。

  1. 如果某些地方只需要寫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稍稍有點彆扭)。

 

本來還想講列舉,結果發現流控制也講了不少,那麼列舉到下一篇再講。

列舉概念很簡單,但我覺得有必要討論的是列舉它簡單的語言定義,和它在實際應用時存在的鴻溝,應該如何填平的問題。

 

覺得文章有意義的話,請動動手指,分享給朋友一起來共同學習進步。

 

歡迎關注本人微信公眾號,更及時的關注最新文章(每週三篇原創文章,以及多篇專題文章):

附文:

C#中的&、&&、|、||的區別

詳解C#break ,continue, return

 

上一篇:解讀經典《C#高階程式設計》第七版 Page32-38.核心C#.Chapter2