書接上文:
4.選擇語句:if語句和switch語句
4.1If語句:statement:兩種格式:A與B
A:If(boolean-expression)embedded-statement(嵌入式語句)
說明:本身A屬於一條if語句,
第一點:在這條if語句里可以嵌入一條語句,被嵌入的語句叫做嵌入語句。
第二點:需要注意的是:既然embedded-statement這是嵌入語句則只可以放入嵌入式語句,非嵌入式語句不能放入如:聲明語句和標簽語句。
第三點:embedded-statement為單數表示只能寫一條嵌入式語句,當一條嵌入式語句就可以表達邏輯時(不用{}),直接寫就行:
if (5 > 3) Console.WriteLine("Hello");
那需要多條語句表達邏輯怎么辦?嵌入式語句里面不是有block塊語句嗎?這就是一般形式if(){}后面這對花括號的由來,它是塊語句。塊語句是一個容器,而編譯器會把塊語句看做一條語句,所以符合if語句的定義。
B:If(boolean-expression)embedded-statement else embedded-statement
習慣上if后面的嵌入式語句叫做true分支,else后面的嵌入式語句叫做false分支。帶有else的語句覆蓋了全部情況。
注意到選擇語句也包含在嵌入式語句中,這意味着,if后面跟的那一條嵌入式語句也可以是if語句這就是所謂的if的嵌套。平時叫優化邏輯的過程叫做代碼的重構。
*無論多么長的if else代碼都是一條語句。
Else if語句的由來,同一個if語句后面的花括號是可以省略的,
把else
{
If{}
}
把else后面的{}省略之后就得到了else if語句,實際上都是else 和if語句。
4.2switch語句:
switch-statement:
switch ( expression ) switch-block
switch-block:
{ switch-sectionsopt }
switch-sections:
switch-section
switch-sections switch-section
switch-section:
switch-labels statement-list
switch-labels:
switch-label
switch-labels switch-label
switch-label:
case constant-expression :
default :
示例:
int Score = 95;
switch (Score/10)
{
case 8:
case 9:
case 10:
Console.WriteLine("A");
break;
case 7:
case 6:
Console.WriteLine("B");
break;
case 4:
case 5:
Console.WriteLine("C");
break;
case 0:
case 1:
case 2:
case 3:
Console.WriteLine("D");
break;
default: Console.WriteLine("Error");
break;
}
特點第一:是case標簽后面要跟一個常量表達式(沒有浮點類型);
第二:case標簽后面只要跟了語句就變成了一個switch-section:則后面一定要跟一個break;如果發現兩個標簽做同一件事情只要把兩個標簽連在一起即可。
*switch選擇語句的強大之處在於判斷枚舉類型時,在switch后面的括號寫完枚舉變量之后按enter鍵就會把枚舉變量所有可能的取值分支都自動補全。
switch (myLevel)
{
case Level.Hight:
break;
case Level.Middle:
break;
case Level.Low:
break;
default:
break;
}
5.try語句
三種格式:
try block({}) catch-clauses
try block finally-clause
try block catch-clauses finally-clause
Catch語句有兩種形式:
catch ( class-type identifieropt ) block:專用的catch語句只能捕捉class-type類型的異常。
catch block:通用的catch語句,全部類型的異常都能捕獲。
詳見語言定義文檔。(*從這些例子很容易看出很多語句后面都跟一對花括號,其實這對花括號是block塊語句)
*作為專業的程序員要經常使用try語句嘗試捕獲異常。祖略一點的就是多條可能出錯的語句使用同一個try...catch結構,精細一點的就是每條可能出錯的語句都配對try....catch語句。
可能會想添加這么多的try...catch結構有必要嗎?對於比較精細的系統如金融系統這是必要的!(多個這樣的結構可以有效提升容錯率)
例如:
try
{
int a = int.Parse(args1);
int b = int.Parse(args2);
}
catch
{
Console.WriteLine("Error");
}
需要注意的是block(塊語句)里面聲明的變量作用域僅為這根塊語句,塊語句外的語句訪問不了塊語句內聲明的變量。如上面的a和b,正確的是:把變量在塊語句外進行聲明。
int a=0;
int b=0;
try
{
int.Parse(args1);
int.Parse(args2);
}
上面的catch語句捕捉的是通用類型的,那如何使用精確的(捕捉特定類型的)catch語句呢?關於可能出現哪種異常可以查看MSDN(微軟開發者網Microsoft Developer Network)文檔。
catch (ArgumentException)
{
Console.WriteLine("Error");
}
catch (FormatException)
{
Console.WriteLine("格式異常");
}
還有一種catch方式是在異常類型后面加上一個標識符(異常類型每個單詞的首字母組成的字符):
catch (ArgumentException ae)
{
Console.WriteLine(ae.Message);
}
catch (FormatException fe)
{
Console.WriteLine(fe.Message);
}
catch (OverflowException ofe)
{
Console.WriteLine(ofe.Message);
}
則出現異常時通過異常標識符會顯示相應的異常信息。
*try還有一種最詳細的形式:加上finally
第一類:我們應該把釋放系統資源的語句放在finally語句里面。以數據庫為例無論try語句是否發生異常我的數據庫連接總能正常關閉,這樣整個軟件系統就不會出問題了。
第二類:我們會在程序的finally語句里面寫程序的執行記錄。比如:程序異常時會輸出默認值0,正常執行也可能出現0怎么判斷這兩種情況呢?就是這種方法的應用,詳見下列代碼:
public int Add(string args1,string args2) { int a=0; int b=0; bool hasError = false; try { int.Parse(args1); int.Parse(args2); } catch (ArgumentException ae) { Console.WriteLine(ae.Message); hasError = true; } catch (FormatException fe) { Console.WriteLine(fe.Message); hasError = true; } catch (OverflowException ofe) { //Console.WriteLine(ofe.Message); throw ofe;//誰調用誰就處理 hasError = true; } finally { if (hasError) { Console.WriteLine("出現異常了"); } else { Console.WriteLine("Done!"); } } int result =checked( a + b); return result; } *throw這個關鍵字:遇到異常時不處理而是丟出去,誰調用誰處理就以”try語句”為例,main方法調用,所以main方法里面處理 catch (OverflowException ofe) { //Console.WriteLine(ofe.Message); throw ofe;//誰調用誰就處理 hasError = true; }
throw關鍵字是比較靈活的,即使不加表示符標識符ofe也知道你要拋出當前捕獲的異常。
這里面丟出去的異常,所以應該在main方法中增添相關的try...catch語句:
try
{
r = c.Add("1222222222222222222222222222222", "0");
}
catch (OverflowException ofe)
{
Console.WriteLine(ofe.Message);
}
Throw一般用不到,當做一個知識即可。
**要養成好習慣,當你覺得某條語句可能出現錯誤的時候就要加try語句進行異常的捕獲;所以程序開發人員要積極的捕獲異常,不要讓漏掉的異常引起程序的崩潰,否則年終你的bug最多就沒有獎金了。
6.迭代語句和跳轉語句
6.1迭代語句:也就是我們常說的循環語句,經常和跳轉語句一起使用。
迭代語句重復執行嵌入語句。
iteration-statement:
while-statement
do-statement
for-statement
foreach-statement
這四種語句在很多情況下是可以互相替換的,但是每種循環語句都有它比較專長的語境。
針對要根據語境選擇可讀性最強的循環語句。
①while語句:循環體可能執行0次:
while-statement:
while ( boolean-expression ) embedded-statement
我們知道出現一條嵌入式語句的地方可以放一條塊語句,塊語句的花括號里面可以寫多條語句,這樣就可以得到表達復雜邏輯的需求。
故while循環可以分為三部分:while關鍵字+布爾類型的表達式的循環條件+循環體(一個嵌入式語句(可以是塊語句))
②do語句:
do 語句按不同條件執行一個嵌入語句一次或多次。
do-statement:
do embedded-statement while ( boolean-expression ) ;
需要注意的是while關鍵字do{}放在后面之后要加分號: while (counter < 10);
*跳轉語句中的:Continue語句和break語句(因為這兩個語句和循環語句結合非常緊密,經常放在一起使用)
continue 語句將開始直接封閉它的 while、do、for 或 foreach 語句的一次新迭代。
直接結束本次循環。
break 語句將退出直接封閉它的 switch、while、do、for 或 foreach 語句。
直接跳出循環不再進行循環。即:跳出while循環后面或者do后面的塊語句那個花括號,直接執行while循環后的下一條語句。
*int x = int.Parse(str1);//這里是利用parse函數把得到的字符串解析成整數,容易出錯常常放在try語句里面。
*需要注意的是迭代語句也屬於嵌入式語句,所以可以實現多重的迭代,所謂的嵌套循環。在多重循環中,continue和break都只會影響到直接包含它的那一層循環,它管不了更外層的循環。
③for(語句)循環:
for-statement:
for ( for-initializeropt ; for-conditionopt ; for-iteratoropt ) embedded-statement
比較適用於計數循環:即某個循環,它的循環次數是固定的,這個循環次數由一個變量控制。
分為三部分:for-initializeropt:for循環的初始化器; for-conditionopt :for循環得以執行的條件,為true才執行,為false則不會執行;for-iteratoropt :(每次執行完循環之后這里都會被執行);
重點為記住執行順序:先執行 for-initializeropt且僅執行一次,再執行 for-conditionopt來判斷我們的for循環體是不是能夠的到執行,當該步為true時for語句循環體才得以執行(一般為塊語句);每次循環體執行完都會反過來執行 for-iteratoropt ;然后再返回 for-conditionopt 判斷條件;即每執行一次循環體判斷一次循環條件,為true繼續執行循環體,為false則停止循環。
一般for-initializeropt都是定義變量並初始化為0; for-iteratoropt 這里都是該變量的++或--;這樣for-conditionopt 遲早會變成false,這樣for循環就結束了。
*要特別注意循環結束后循環條件的值。一定是不符合循環條件的最小值例如: for (int i = 0; i < 10; i++)則執行完循環語句后i的值為10,而不是9;
*平時寫代碼特別要注意可讀性和代碼是不是容易錯。
*注意到 for循環()內三個語句都有opt說明這三個語句都是可以省略的,不過現實中不會這么寫,只有在bt的面試題會考。
*應該多做算法題,並且沒做完一道算法題都要進行總結,做多了會發現其實套路就那些,着對面試很有幫助,面試時壓力大不可能現場想新題,這時候就要想這道題是那種提醒,我之前做的時候是怎樣總結的。
④foreach語句:
foreach 語句用於枚舉一個集合的元素(即從頭到尾一個挨一個的把集合元素訪問一遍,即我們常說的遍歷這個集合),並對該集合中的每個元素執行一次相關的嵌入語句。
換一句話說,使用foreach語句時每訪問集合的一個元素就執行一次循環語句,遍歷完了集合元素,這個循環就結束了。
*知識點1:什么樣的集合可以被遍歷:帶[]的類型都是數組類型,其基類都是Array類,以下為Array類的定義:
public abstract class Array : ICloneable, IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable
可見它實現了許多接口,接口好認,開頭為大寫I的一般都是(Interface)。在C#中所有實現了IEnumerable這個接口的類就是可以被遍歷的集合。
IEnumerator GetEnumerator();
這個接口只有一個方法:獲得這個集合的迭代器,即C#中所有能夠被迭代的集合都可以獲得自己的迭代器。
*知識點2:迭代器(Enumerator):先講一個禪宗的故事”指月”說的是順着我手指的方向看看到的就是月亮,而我的手指本身不是月亮。假如天上有十個月亮,這個時候我的手指就相當於月亮這個集合的迭代器,我們可以從頭到尾一個一個地指,每指到一個月亮的時候,你就可以去訪問這個月亮,這就是迭代器。
那如何使用迭代器對集合進行遍歷呢?
int[] myArray = new int[] { 1,2,3,4,5,6};
IEnumerator enumerator=myArray.GetEnumerator();
while (enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current);
}
enumerator.Reset();
while (enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current);
}
當沒有enumerator.Reset();時調用兩次只輸出一次結果是因為,當第一次遍歷完之后,迭代器已經指向了集合的最后一個元素,這時候再調用enumerator.MoveNext()值為false;
調用了enumerator.Reset();把迭代器重置則會輸出兩次;
之所以對遍歷講這么深是因為Foreach是對集合遍歷的簡記法。現在再來看看foreach語句的定義:
foreach-statement:
foreach ( local-variable-type identifier in expression ) embedded-statement
圓括號里local-variable-type identifier:先聲明一個迭代變量,就相當於迭代器,in也是個關鍵字,expression是一個集合,有了迭代器和集合就可以對集合中的元素一個一個地訪問了,每訪問到一個元素就執行foreach語句的循環體,就是后面這句嵌入式語句(同樣可以是一個塊語句,用塊語句里面的多條語句來組成復雜的邏輯);舉例:
List<int> myArray2 = new List<int> { 1,2,3,4,5,6,7};
foreach (var count in myArray2)
{
Console.WriteLine(count);
}
鼓勵使用var編譯器會自動推斷集合的類型, count 為自定義的迭代器名字是集合中元素的別稱。可見運用foreach遍歷集合十分方便。
*再次強調foreach最佳應用場合是對集合進行遍歷
6.2跳轉語句:
跳轉語句用於無條件地轉移控制。
jump-statement:
break-statement
continue-statement
goto-statement
return-statement
throw-statement
看到throw就想起try語句,他很靈活,后面可以跟catch也可以不跟。
*使用return需要注意的地方。
盡早return;例如:同一個方法原來的版本:
static void Greeting(string name)
{
if (!string.IsNullOrEmpty(name))
{
Console.WriteLine("Hello!{0}",name);
}
}
盡早return的版本:
static void Greeting(string name)
{
if (string.IsNullOrEmpty(name))
{
return;
}
else
{
Console.WriteLine("Hello!{0}", name);
}
}
為什么要這樣寫呢?這樣寫的好處就在於你可以讓讀這條代碼的人立刻就鑒別出來這個參數在什么情況下是有問題的,而且還可以避免整個方法寫出來”頭重腳輕”,如果: Console.WriteLine("Hello!{0}",name);
這里的邏輯不是只有有一行而是很多行,我么多行就不必要包含在一個if語句當中,條件判斷if語句就會變得非常短小,一旦發現某些參數或條件不合格的時候立刻return出去了,這樣整個方法的結構就非常清晰了。這就是盡早return原則。
*下一個需要注意的是:如果你的方法的返回值不是void類型,而這個方法體里面使用了選擇語句,那必須保證選擇語句的每一個分枝當中,都能夠讓這個方法return:如該方法
static string WhoseWho(string name)
{
if (name=="M")
{
return "Yes";
}
else
{
return "No";
}
我們知道if和else可以覆蓋100%的情況。這就是我們說的要保證方法一定能夠return。
回到開頭的那張總圖片:綠色虛線以下的不講,要么不常用要么對於初學者太難了。比如:空語句,用的不多;標簽語句總是與goto一起使用goto語句並不是主流的語句了所以用的不多;checked和unchecked語句,可以當做操作符或者語句使用,黨作為語句使用時:
Checked{,,,}花括號為塊語句里面是要檢查的語句;三個比較難的就是:using(接口)、yield(集合)、和lock語句(多線程)。
*作為一個程序員學習能力體現在三個地方:
一個是看書讀文檔;
一個是動手寫代碼;
還有一個是熟練地使用搜索引擎。
