第 15 講:程序結(jié)構(gòu)(一):條件結(jié)構(gòu)
我們前面用了六個(gè)文章介紹了運(yùn)算符的使用。今天我們進(jìn)入新的板塊:程序控制結(jié)構(gòu)。C 語(yǔ)言里,程序控制結(jié)構(gòu)一共有四種:順序結(jié)構(gòu)、條件結(jié)構(gòu)、循環(huán)結(jié)構(gòu)和跳轉(zhuǎn)結(jié)構(gòu)。C# 里一共是 5 種:順序結(jié)構(gòu)、條件結(jié)構(gòu)、循環(huán)結(jié)構(gòu)、跳轉(zhuǎn)結(jié)構(gòu)和異??刂平Y(jié)構(gòu)。順序結(jié)構(gòu)是最基礎(chǔ)的結(jié)構(gòu),在前面的文章早就體現(xiàn)過(guò)了,只是沒(méi)有提出它的名稱。只要一段代碼從上到下依次執(zhí)行的過(guò)程,我們就把這段代碼稱為順序結(jié)構(gòu)。
Part 1 if
的用法
1-1 if
語(yǔ)句
前面的內(nèi)容我們無(wú)法控制代碼在指定條件的時(shí)候,才做一些工作。當(dāng)我們需要這樣的用法的話,我們將使用 if
語(yǔ)句來(lái)控制。
格式是這樣的:
有這樣兩種寫法。我們來(lái)寫一個(gè)例子。
在這個(gè)例子里,我們?cè)?if (a > b)
之后使用大括號(hào),將 Console.WriteLine
完整包裹起來(lái)。這表示,用大括號(hào)包裹起來(lái)的所有內(nèi)容,都屬于“當(dāng) a > b
成立的時(shí)候,才會(huì)執(zhí)行的邏輯”。
當(dāng) a > b
的結(jié)果是 false
(即不成立)的時(shí)候,不管大括號(hào)里有多少東西,都不會(huì)得到執(zhí)行,會(huì)被完全忽視掉。顯然,這個(gè)例子里,30 是比 40 小的,所以條件并不成立。因此,你在執(zhí)行過(guò)程之中,什么輸出都看不到。
順帶一說(shuō),既然用到了大括號(hào),那么里面的內(nèi)容就需要縮進(jìn)來(lái)保證代碼的整潔。如果大括號(hào)內(nèi)的內(nèi)容不縮進(jìn)的話,看起來(lái)就很奇怪。當(dāng)然,它并不影響編譯程序和運(yùn)行程序就是了。
1-2 else
語(yǔ)句
顯然,這樣的格式并不能滿足我們正常的需求。前文的例子我們其實(shí)還想看到另外一個(gè)輸出內(nèi)容,“b
比 a
大”,但在代碼里沒(méi)有體現(xiàn)。因此,else
就派上用場(chǎng)了。
和 if
差不多,else
后也是跟一個(gè)大括號(hào),或者一個(gè)單獨(dú)的語(yǔ)句。這部分表示在 if
條件并不成立的時(shí)候,才會(huì)得到執(zhí)行。
如果我們這么寫代碼的話,這就表示,當(dāng) if (a > b)
里的 a > b
條件成立的時(shí)候,會(huì)輸出 a
比 b
大的內(nèi)容;但是如果不成立,就會(huì)輸出 b
大于 a
的內(nèi)容。
if
-else
整體的話,if
的部分和 else
的部分并不會(huì)都執(zhí)行,也必然必須執(zhí)行至少一個(gè)部分。這就是 if
-else
配合一起使用的時(shí)候的特點(diǎn)。
當(dāng)然,前文說(shuō)過(guò),空格是不影響編譯和運(yùn)行程序的,因此我們甚至可以寫在一行:
這么寫依然是沒(méi)有問(wèn)題的。代碼寫成什么樣看的是個(gè)人的習(xí)慣,只要邏輯沒(méi)有問(wèn)題就行。
1-3 else
后也不一定非得是大括號(hào)和語(yǔ)句
a > b
的對(duì)立情況就一定是 b > a
嗎?當(dāng)然不是,還可以相等。但是我們剛才沒(méi)有體現(xiàn)這一點(diǎn)。下面我們來(lái)試著添加這個(gè)部分:
當(dāng) a > b
條件不成立的時(shí)候,我們會(huì)想著走 else
這一段代碼。而 else
里又是一個(gè) if
判斷:a == b
a > b
條件是不是成立的。如果不成立,就會(huì)繼續(xù)判斷 a == b
這個(gè)條件。因此,實(shí)際上這種寫法就解決了前面說(shuō)的,忘記判斷 a == b
的問(wèn)題。
不過(guò),這個(gè)寫法還是有點(diǎn)丑。C# 可以允許我們?nèi)サ?else
的大括號(hào):
這個(gè)寫法是沒(méi)有問(wèn)題的。C# 是知道,下方的 if
-else
是一個(gè)整體,因此不需要大括號(hào)就知道整個(gè)代碼里的 else
就完整包含了下面一大坨內(nèi)容。但是,實(shí)際上還是有點(diǎn)丑。不如我們把 else
后直接跟上 if
:
這樣就很好了。從這個(gè)角度來(lái)說(shuō),我們一下就可以看出邏輯:先 a > b
;如果不滿足就 a == b
;再不滿足就執(zhí)行最下面的 else
1-4 它和條件運(yùn)算符的關(guān)聯(lián)
最開(kāi)始,我們說(shuō)到過(guò)一個(gè)東西,叫條件運(yùn)算符。條件運(yùn)算符的邏輯完全類似于這里的 if
-else
的過(guò)程。不過(guò)問(wèn)題在于,它們有區(qū)別嗎?
條件運(yùn)算符用來(lái)表示和賦值,因此 ?
后和 :
后的部分全都是一個(gè)表達(dá)式(所謂的表達(dá)式,就必須反饋一個(gè)結(jié)果數(shù)值出來(lái);而語(yǔ)句則不一定要反饋結(jié)果,它可以像是 Console.WriteLine
那樣,單獨(dú)成一個(gè)語(yǔ)句來(lái)用)。而 if
-else
則可以跟語(yǔ)句或者是賦值表達(dá)式。我們來(lái)看一下。
這是將 a
和 b
較大的數(shù)值賦值給 c
的過(guò)程。如果要寫成 if
-else
,就得這樣:
或者直接寫到一行上去:
不管怎么做,代碼都比條件運(yùn)算符要復(fù)雜。因此,我們建議用條件運(yùn)算符而不是用 if
-else
1-5 永真和永假條件
有沒(méi)有想過(guò),既然這里我們傳入的是一個(gè) bool
的表達(dá)式,那么我們直接寫 true
和 false
會(huì)如何。
或者
猜都猜得到,只要條件是 true
就會(huì)進(jìn)去執(zhí)行。因此 if (true)
等價(jià)于根本沒(méi)寫條件判斷。而 if (false)
if
里面的語(yǔ)句永遠(yuǎn)都得不到執(zhí)行。
Part 2 switch
的用法
很顯然,前文的 if
-else
是無(wú)敵的。但是很遺憾的是,對(duì)于一些特殊的條件判斷,if
-else
怎么寫都很臃腫。比如我們輸入一個(gè)月份數(shù)值,然后判斷月份到底有多少天。那么用 if
就得這樣:
我且不說(shuō)其它的內(nèi)容,就判斷 month
的信息,也顯得很臃腫。此時(shí),我們這里介紹一個(gè)新的語(yǔ)法格式:switch
語(yǔ)句。
2-1 switch
語(yǔ)句
switch
語(yǔ)句的用法是這樣的:
這個(gè)寫法可能不是很好懂。我們直接上代碼:
貌似……好像代碼沒(méi)有減少多少。不過(guò)更好看了,因?yàn)?month
直接寫到 switch
上用來(lái)表示“我這里就是按 month
case
里寫的就是 month
的所有可能數(shù)值。default
則表示當(dāng)前面所有數(shù)值都不是 month
現(xiàn)在的數(shù)值的時(shí)候,就走這里。
請(qǐng)注意一下,每一個(gè) case
語(yǔ)句最后,都要跟一個(gè) break;
,這是因?yàn)檫@是為了讓每一個(gè) case
斷層。C 語(yǔ)言里,沒(méi)有寫 break
就會(huì)導(dǎo)致執(zhí)行的時(shí)候出現(xiàn)潛在的問(wèn)題。C# 沿用了這種機(jī)制,但防止你誤用代碼,所以不寫 break
會(huì)產(chǎn)生編譯錯(cuò)誤,提示你必須加了 break;
之后才能繼續(xù)執(zhí)行。C# 里,break;
語(yǔ)句遇到后,會(huì)自動(dòng)跳出 switch
的內(nèi)容。比如這個(gè)例子里,會(huì)自動(dòng)跳轉(zhuǎn)到第 27 行代碼執(zhí)行。
2-2 同執(zhí)行語(yǔ)句簡(jiǎn)化
顯然,我們發(fā)現(xiàn)當(dāng) 1、3、5、7、8、10、12 月份的時(shí)候,day
都是 31;而當(dāng) 4、6、9、11 的時(shí)候,day
都是 30。switch
還有一個(gè)強(qiáng)勁的功能就是,簡(jiǎn)化 case
。我們只看 switch
這一塊,代碼可以簡(jiǎn)化成這樣:
這樣的話,就更簡(jiǎn)單一些了。如果寫 if
||
來(lái)連接條件。比如說(shuō)第 3 行轉(zhuǎn)成 if
就得寫成這樣:
顯然,就很丑。
2-3 字符串的 switch
在 C# 里,因?yàn)橛凶址@種類型,因此 switch
還可以對(duì)字符串進(jìn)行判斷。
case 字符串
的格式。確實(shí),C# 也是允許這么做的。
由于字符串的每一個(gè)字符都不一樣的關(guān)系,就算只有大小寫不同的兩個(gè)字符串也屬于兩個(gè)不同的字符串,因此我們這里在例子程序中是分開(kāi)成兩個(gè)情況作判斷的。
2-4 很遺憾,布爾量沒(méi)有 switch
這顯然是廢話。bool
類型的數(shù)值往往只有 true
和 false
兩種可選情況,那么你要寫成 switch
語(yǔ)句的話,你只能寫成這樣:
可問(wèn)題在于,case true
和 case false
就已經(jīng)構(gòu)成了 bool
類型的所有可能取值的情況。那么,我是不是就意味著我可以把這里的 case false
替代為 default
呢?那么,我這么寫和前文的語(yǔ)義是一樣的嗎?
如果真的是一樣的,那么我這么寫代碼不就很奇怪了嗎?
是的??紤]到這種寫法的語(yǔ)義格式的復(fù)雜性,以及使用場(chǎng)景的問(wèn)題,C# 并未對(duì)布爾型變量開(kāi)放允許使用 switch
語(yǔ)句。雖然很遺憾,但也是合情合理的情況。
Part 3 混用 switch
和 if
前文我們沒(méi)有判斷 2 月份在平年還是閏年。如果我們加上這個(gè)判斷的話,就這么寫:
case
最后必須跟一個(gè) break;
,因此就算我們寫了一大坨東西,最后的 break;
也是不可少的。當(dāng)然了,你也可以這么寫:
這么寫呢,就是長(zhǎng)一點(diǎn),但是用的是條件運(yùn)算符。
Part 4 if
和 switch
的選擇
如果你在寫代碼的時(shí)候,肯定會(huì)遇到“我到底用 if
好,還是 switch
好”的問(wèn)題。有一個(gè)很好的判斷標(biāo)準(zhǔn)是,。
比如前面的例子,我們對(duì) month
進(jìn)行數(shù)值的確認(rèn),顯然是用 switch
更合理;但是如果是其它的情況,我們都應(yīng)該采用 if
來(lái)表示。當(dāng)然了,如果可能數(shù)值過(guò)多的話,我們就不建議用 switch
了。