第三章 流程控制與例外處理 資訊教育研究室 製作 注意:本投影片僅供上課使用,非經同意,請勿散播或轉載。
3.1 選擇結構 三種方式: 2. if… else if… else (多重選擇兩種以上) 3. switch (多重選擇兩種以上)
3.1.1 if…else 選擇陳述式 ( price>=1000)
If (price>=1000) { discount=0.8; } If (price>=1000) discount=0.8; If (條件式) 陳述式;
【例1】 若單價(price)大於等於1,000元,折扣(discount)八折, 否則折扣九折。 if ( price>=1000) { discount=0.8 ; // price大於等於1,000執行此敘述 } else discount=0.9 ; // price小於1,000 執行此敘述
年齡(age)是10歲(含)以下或60歲(不含)以上則票價(price)為100元,否則為200元。寫法如下: ☆【例2】 年齡(age)是10歲(含)以下或60歲(不含)以上則票價(price)為100元,否則為200元。寫法如下: if (age<=10 || age>60) { price=100 ; } else price=200 ;
巢狀if 若 if 或 else 程式區塊內,還有 if…else 陳述式。 試寫一個程式由鍵盤輸入兩個整數(num1和num2), 若num1=num2,則顯示 “num1=num2“。 若num1>num2,則顯示 “num1>num2“。 若num1<num2,則顯示 “num1<num2“。
// FileName: ifelse1.sln 01 namespace ifelse1 02 { 03 class Program 04 { 05 static void Main(string[] args) 06 { 07 int num1, num2; 08 Console.Write("請輸入第一個整數(num1) : "); 09 num1 = int.Parse(Console.ReadLine()); 10 Console.Write("請輸入第二個整數(num2) : "); 11 num2 = int.Parse(Console.ReadLine()); 12 if (num1 == num2) 13 { 14 Console.WriteLine("{0} = {1}", num1, num2); 15 }
else { 18 if (num1 > num2) 19 { 20 Console.WriteLine("{0} > {1}", num1, num2); 21 } 22 else 23 { 24 Console.WriteLine("{0} < {1}", num1, num2); 25 } 26 } 27 Console.Read(); 28 } 29 } 30 }
3.1.2 if…else if…else 多重選擇陳述式 有兩個以上的條件式,需要連續做判斷時
延續上一範例,改用if … else if … else多重選擇陳述式 來撰寫兩數比大小。
本週作業 // FileName: ifelseif1.sln 01 static void Main(string[] args) 02 { 03 int num1, num2; 04 Console.Write("請輸入第一個整數(num1) :"); 05 num1 = int.Parse(Console.ReadLine()); 06 Console.Write("請輸入第二個整數(num2) :"); 07 num2 = int.Parse(Console.ReadLine()); 08 if (num1 == num2) 09 { 10 Console.WriteLine("{0} = {1}", num1, num2); 11 } 12 else if (num1 > num2) 13 { 14 Console.WriteLine("{0} > {1}", num1, num2); 15 } 16 else 17 { 18 Console.WriteLine("{0} < {1}", num1, num2); 19 } 20 Console.Read(); 21 }
3.1.3 switch多重選擇陳述式 使用上差異 if … else if … else 可使用多個不同條件式。 使用太多 if 使得程式看起來複雜且不易維護, switch 多重選擇陳述式則不會。
Case 各種寫法
試使用 switch ,由鍵盤輸入現在的月份(1~12), 輸入5,表 5月份,由程式判斷 5月份 是屬於 哪一季? 顯示 ”現在是第二季”; 若輸入值超出範圍,則顯示 “... 輸入值超出範圍...”。
// FileName: switch1.sln 01 static void Main(string[] args) 02 { 03 string month; 04 Console.Write("=== 請輸入現在的月份: "); 05 month = Console.ReadLine(); 06 switch (month) 07 { 08 case "1": 09 case "2": 10 case "3": 11 Console.WriteLine(" \n ... 現在是第一季..."); 12 break; 13 case "4": 14 case "5": 15 case "6": 16 Console.WriteLine(" \n ... 現在是第二季..."); 17 break;
18 case "7": 19 case "8": 20 case "9": 21 Console.WriteLine(" \n ... 現在是第三季..."); 22 break; 23 case "10": 24 case "11": 25 case "12": 26 Console.WriteLine(" \n ... 現在是第四季..."); 27 break; 28 default: 29 Console.WriteLine(" \n ... 輸入值超出範圍...."); 30 break; 31 } 32 Console.Read(); 33 }
本週作業 練習把ifelseif1.sln程式,略作修改後(參考Page 3-8),儲存成學號,最後壓縮,並E-mail至jllioutw@hotmail.com Mail主旨(主題 ,Subject):學號
01 static void Main(string[] args) 02 { 03 int num1, num2; 04 Console.Write(“Please input first number :"); 05 num1 = int.Parse(Console.ReadLine()); 06 Console.Write(“Please input second number :"); 07 num2 = int.Parse(Console.ReadLine()); 08 09 if (num1 == num2) 10 { Console.WriteLine(“\n {0} = {1}", num1, num2); } 11 12 else if (num1 > num2) { Console.WriteLine(“\n {0} > {1}", num1, num2); } 14 15 else 16 { Console.WriteLine(“\n {0} < {1}", num1, num2); } 17 18 Console.Read(); 19 }
The End
3.2 重複結構 重複結構 是指設計程式時需要將某部份程式 區塊重複執行指定的次數,或 一直執行到不滿足條件為止。 重複結構亦稱 迴圈 (Loop)。 for陳述式 指定次數者稱為「計數器」控制迴圈。 while陳述依條件者稱為「條件式」控制迴圈。 三種結構的流程圖如下圖所示:
3.2.1 for 迴圈 計數器控制迴圈以 for 的 左大括號開始,以右大括號 結束。 語法: Count=count+1
For (count=1 ; count<=5 ; count++) { …… continue ; …... break ; } 使用 continue ,立即跳回 for 的開頭繼續執行。 中途欲離開 for 迴圈,使用 break 陳述式。 For (count=1 ; count<=5 ; count++) { …… continue ; …... break ; }
一般 for 迴圈的常用寫法:(Page 3-13 ,3-14) for ( k=1 ; k<= 5 ; k++) { 程式區塊 } k = 1、2、3、4、5 共執行迴圈內的程式區塊5次。 for ( k=1 ; k<=5 ; k+=2) k = 1、3、5 共執行迴圈內的程式區塊3次。 初值、增值可為小數 for ( k=-0.5 ; k<=1.5 ; k+= 0.5) k = -0.5、0、0.5、1.0、1.5 共執行迴圈內程式區塊5次。 K = K + 1 K = K + 2 條件 K = K + 0.5
for (k=x ; k<=y+9 ;k+=2) 若x=1、y=-2,則 k = 1,3,5,7 共執行迴圈內的程式區塊 4 次。 增值採遞減 for ( k=6 ; k>=1 ; k-=2) k = 6、4、2 共執行迴圈內的程式區塊3次。 若初值、增值都有兩個以上,中間使用逗號分開: for (x=1, y=5 ; x<3 && y>2 ;x++ , y-- ) x=1 & y=5; x=2 & y=4; 共執行迴圈內的程式區塊2次。 初值和終值可以為運算式 for (k=x ; k<=y+9 ;k+=2) 若x=1、y=-2,則 k = 1,3,5,7 共執行迴圈內的程式區塊 4 次。 無窮迴圈 for ( ; ; ) 條件 K = K - 2 X = X + 1 , y = y - 1 K = 1 ; K <= 7 ; K = K + 2
試求下列級數的和。 = 3 + 5 + 7 + 9 + 11 = ? x=1 x=2 x=3 x=4 x=5
sum = sum + 2*X+1 x = 1 , sum = 0 + 3 = 3 x = 2 , sum = 3 + 5 = 8 // FileName: series.sln 01 static void Main(string[] args) 02 { 03 int x, sum = 0; 04 Console .WriteLine ("\n === 求級數的總和 ==== \n"); 05 Console.WriteLine (" x 2x+1 "); 06 Console .WriteLine (" ======= ======= "); 07 for (x = 1; x <= 5; x++) 08 { 09 Console .WriteLine (“ {0} {1} ”, x, 2 * x + 1); 10 sum += 2 * x + 1; 11 } 12 Console .WriteLine (" ---------------------------- "); 13 Console .WriteLine (" 此級數總和為 : {0} \n", sum); 14 Console.Read(); 15 } sum = sum + 2*X + 1
本週作業 3.2.2 巢狀迴圈 i=1 i=2 i=3 i=4 i=5 若迴圈內還有迴圈就構成巢狀迴圈,一般應用在二維資料列表。 (Page 3-16) 若迴圈內還有迴圈就構成巢狀迴圈,一般應用在二維資料列表。 下例外迴圈的變數,每個變數值印五次後,再將游標移到 下一列最前面,總共印五列。 for (int i = 1; i <= 5; i++) { for (int k = 1; k <= 5; k++) Console.Write("{0} ", i); } Console.WriteLine(); 外迴圈 i=1 i=2 i=3 i=4 i=5 內迴圈
3.2.3 前測式迴圈 前測式迴圈 就是將 條件式 放在迴圈的 最前面。 依條件式的真假決定是否進入迴圈: 若滿足條件將迴圈內的程式區塊執行一次,然後 再回迴圈最前面條件式。 不滿足時才離開迴圈。 前測式迴圈,若第一次進入迴圈時便不滿足條件式, 馬上離開迴圈,連一次都沒執行迴圈內的程式區塊。
3.2.4 後測式迴圈 後測式迴圈 就是將 條件式 放在迴圈的 最後面。 第一次不用檢查條件式,直接進入迴圈執行 裡面的程式區塊,才判斷條件式的真假: 若滿足條件會將迴圈內的程式碼執行一次, 再檢查位於迴圈最後面的條件式。 一直到不滿足條件時才離開迴圈。 此種架構迴圈內的程式區塊至少會執行一次。
試寫一個使用前測式迴圈計算階乘的程式,由使用者 先輸入一個整數,接著再計算該數的階乘值。 如下圖輸入5,結果計算出5!=120。
factorial = factorial * num num = 5 , factorial = 1 * 5 = 5 (Page 3-18) factorial = factorial * num num = 5 , factorial = 1 * 5 = 5 num = 4 , factorial = 5 * 4 = 20 num = 3 , factorial = 20 * 3 = 60 num = 2 , factorial = 60 * 2 = 120 num = 1 , factorial = 120 * 1 = 120 // FileName: factorial1.sln 01 static void Main(string[] args) 02 { 03 int keyin, num, factorial = 1; 04 Console.Write("請輸入整數: "); 05 keyin = int.Parse(Console.ReadLine()); 06 num = keyin; 07 while (num >= 1) 08 { 09 factorial *= num; 10 num - = 1; 11 } 12 Console.WriteLine("{0}! = {1} ", keyin, factorial); 13 Console.Read(); 14 } factorial = factorial * num num = num - 1
本週作業 練習把series.sln程式(參考Page 3-14, 3-15),略作修改後儲存成學號,最後壓縮,並E-mail至jllioutw@hotmail.com Mail主旨(主題 ,Subject):學號
The End
3.4 程式除錯 當一個程式在編譯時沒錯誤發生,在執行階段若無法得到預期的結果,表示發生邏輯上錯誤 邏輯錯誤 並不是語法錯誤,而是程式的流程、運算式、變數誤用等錯誤 此時就需要使用「區域變數」視窗來做逐行偵錯,觀察 每行執行結果是否正確?以找出發生錯誤的地方。
3.4.1 逐行偵錯 Visual C# 2010提供「區域變數」視窗來評估變數和運算式,並保存其結果。 可用「區域變數」視窗來編輯變數或暫存器的數值。 下例透過 for 迴圈來學習程式如何做逐行偵錯。
k = k + 5 sum = sum + i sum = 0+1+2+3 上面程式執行時,請按照下面步驟來對程式做逐行偵錯工作。 // FileName: debug1.sln 01 static void Main(string[] args) 02 { 03 int i, k, sum = 0; 04 k = 11; 05 for (i = 1; i <= 3; i++) 06 { 07 k += 5; 08 sum += i; 09 Console.WriteLine("i={0} , k={1}", i, k); 10 } 11 Console.WriteLine("i={0} , sum = {1} ", i, sum); 12 Console.Read(); } 上面程式執行時,請按照下面步驟來對程式做逐行偵錯工作。 k = k + 5 sum = sum + i sum = sum + i 1 = 0 + 1 3 = 1 + 2 6 = 3 + 3 sum = 0+1+2+3
Step1 先點選功能表的【偵錯(D)/逐步執行(I)】或直接按 鍵,此時在Main()方法的下一行 ”{” 會出現 向右箭頭, 表示下次執行由此行陳述式開始,進入逐行偵錯:
Step2 點選【偵錯(D)/視窗(W)/區域變數(L)】開啟「區域變數」監看視窗。
Step3 按 功能鍵三次,已執行過 k = 11,區域變數 監看視窗內的 K 值由 0 11。
Step4. 接著按 功能鍵六次,執行 sum += 1 和 Console Step4 接著按 功能鍵六次,執行 sum += 1 和 Console..., 此 時 區域變數監看視窗內的 sum 由 01, i 由 01, k 由1116。並執行Console陳述式, 此時會將 i 和 k 值 按照設定顯示格式顯示在主控台視窗。
Step5 接著按 功能鍵一次,跳回執行for Step5 接著按 功能鍵一次,跳回執行for...陳述式, 將i值12,以此類推下去,便可看到各陳述式中, 各變數的變化情形。 若要中斷逐行偵錯,將尚未執行的陳述式一次執行 完畢,可按 開始偵錯鈕,會顯示最後結果。
3.4.2 設定中斷點 上節逐行偵錯是屬於細部除錯。 C# 2010另提供中斷點設定做大範圍除錯。 設定中斷點做法: 在程式中欲監看的陳述式前面設定中斷點,程式每次執行到所設定的中斷點會停止執行,此時可透過區域變數監看視窗或移動滑鼠到該變數上會顯示該變數目前的值。
Step1設定中斷點 先移動滑鼠到下圖程式中有 中斷點圖示處按一下, 將指定的三行陳述式設成中斷點。有設定中斷點的 陳述式會預設紅色底顯示:
Step2 開始偵錯 功能表【偵錯(D)/開始偵錯(S)】或直接按 功能鍵, 開始執行到第一個中斷點處暫停。 此時 k+=5 前出現 向右箭頭,表下次由此陳述式開始 往下執行。 可從「區域變數」監看視窗觀看目前變數的值。 若螢幕未出現「區域變數」監看視窗,行功能表【偵錯(D)/視窗 (W)/區域變數(L)】開啟「區域變數」視窗。
Step3 直接觀看 直接移動滑鼠到編碼視窗任一個變數上停一會兒, 在該變數的右下方出現目前該變數的值。
Step4 繼續執行 按 功能鍵或 鈕,執行到 ”}” 符號便暫停執行。 各行陳述式中變數變化情形如下圖箭頭。
Step6 停止偵錯 若中途欲停止偵錯可以執行【偵錯(D)/停止偵錯(E)】 或直接按 停止偵錯圖示即可。
3.5 例外處理 例外(Exception) 就是指當程式在執行時期所發生的錯誤。 設計良好的錯誤處理程式碼區塊,可讓程式更為穩定,且更不易因應用程式處理此類錯誤而當機。
例外處理主要由 try、catch、throw、finally 四個關鍵字構成。 方式是將要監看是否發生錯誤的程式區塊放在 try 區塊內 當 try 區塊內的任何陳述式執行時發生錯誤,該例外會被丟出 throw,在程式碼中利用 catch 抓取此例外情況 C# 會由上而下逐一檢查每個 catch 陳述式,當找到符合的 catch 陳述式,會將控制權移轉到該 catch 陳述式內程式區塊 的第一列陳述式去執行。 當該 catch 程式區塊執行完畢,不再繼續往下檢查 catch 陳述式 直接跳到 finally 內執行 finally 程式區塊。 若未找到符合的 catch 陳述式,最後也會執行 finally 內的 finally 程式區塊後才離開 try。
try { [try程式區塊] // 需例外處理的程式區塊 } catch(exception1 變數ex) [catch程式區塊] // 當需例外處理的程式區塊發生錯誤, 符合exception1時 // 執行此程式區塊1; catch(exception2 變數ex) 符合exception2時 // 執行此程式區塊2; finally [finally程式區塊] //無論是否發生例外, 都會執行此程區塊
試寫一個會發生除數為零 DivideByZeroException 例外程式 或直接開啟下面 try1.sln 範例程式。本程式中先宣告i、k、p 為整數變數,並設定 i 初值為5,k 初值為0。 當執行 i/k 時會發生除數為零的 DivideByZeroException例外 此時程式即會終止執行。
// FileName: try2.sln 01 static void Main(string[] args) 02 { 03 int i, k, p; 04 i = 5; 05 k = 0; 06 try 07 { 08 p = i / k; 09 } 10 catch (Exception ex) 11 { 12 Console.WriteLine("發生例外"); 13 } 14 finally 15 { 16 Console.WriteLine(".... 結束程式執行!! ..."); 17 } 18 Console.Read(); 19 }
註:Message屬性會取得目前例外的訊息。 // FileName: try2.sln 01 static void Main(string[] args) 02 { 03 int i, k, p; 04 i = 5; 05 k = 0; 06 try 07 { 08 p = i / k; 09 } 10 catch (DivideByZeroException ex) 11 { 12 Console.WriteLine(ex.Message); 13 } 14 catch (Exception ex) 15 { 16 Console.WriteLine(ex.Message); 17 } 18 finally 19 { 20 Console.WriteLine(".... 結束程式執行!! ..."); 21 } 22 Console.Read(); 23 } 註:Message屬性會取得目前例外的訊息。 註:catch(Exception ex) 最好放在所有catch 陳述式的最後面及 finally 前面。
Take a Break ….
下表列出幾個例外物件常用的屬性與方法,透過 這些方法可供你了解一些例外的資訊。
// FileName: try3.sln 01 static void Main(string[] args) 02 { 03 int i, k, p; 04 i = 5; 05 k = 0; 06 try 07 { 08 p = i / k; 09 } 10 catch (DivideByZeroException ex) 11 { 12 Console.WriteLine("例外訊息:{0}", ex.Message); 13 Console.WriteLine("發生例外的函式:{0}", ex.StackTrace); 14 Console.WriteLine("發生例外的物件:{0}", ex.Source); 15 Console.WriteLine("發生例外的物件型別:{0}", ex.GetType()); 16 Console.WriteLine("發生例外的文字說明:{0}", ex.ToString()); 17 } 18 finally 19 { 20 Console.WriteLine(".... 結束程式執行!! ..."); 21 } 22 Console.Read(); 23 }