--- tags: 視窗程式設計 --- # 計算機程式:優化程式碼 現在我們大致已經將基本的計算機程式完成,雖然程式跑起來都是正常無誤的,但是仍有改善空間。 這個章節也是利用基本函式的觀念,幫助同學理解函式的運用。 ## 複習一下:建立函式(方法) 還記得以前介紹過的「函式」概念嗎?如果不知道什麼是函式,你會遇到以下問題: 1. 你需要重複貼上好多程式碼 2. 因為每一個單位轉換都需要仔細的改程式,如果要測試,很麻煩 3. 如果有錯,還要把所有程式都找過一次,很痛苦 把一些看起來很類似的程式,通通打包起來,寫在某個地方,然後如果我要使用的時候再來「呼叫」這段打包好的程式?就不用一直重複複製貼上這些類似的程式。這就是「**函式(方法)**」概念,簡單來說,函式就是把常用的程式碼打包裝在一個區塊中,方便我們在寫程式時隨時呼叫、使用。 :::info 適度的將重複性高的程式「打包」成函式(方法),能讓程式看起來更簡潔! ::: 函式就像是販賣機,你設計好一個函式,可以給予不同的輸入值,經過函式內的程式運算,得到值。以下是一個販賣機的「想像函式」: ```csharp= 販賣機($_money, $_number){ $drink = "沒有飲料"; if ($_money == 30){ switch ($_number){ case 0: $drink = "可樂"; break; case 1: $drink = "綠茶"; break; } } return $drink; } 販賣機(30, 1); // 販賣機掉出綠茶 販賣機(20, 1); // 販賣機不會有飲料出來 販賣機(30, 3); // 販賣機不會有飲料出來 ``` ### 函式(方法)的語法 通常函式(方法)可以寫成四種型式: 1. 沒有參數也沒有回傳值 ```csharp= void 函式名稱() { 你的程式碼; } 函式名稱(); //執行這個函式 ``` 如果你看到你的方法有void,代表:沒有回傳值 2. 有參數、沒有回傳值 ```csharp= void 函式名稱(參數型別 參數1, 參數型別 參數2 ...) { 你的程式碼; } 函式名稱(123, "佛光大學"); //執行這個函式 ``` 3. 有參數、有回傳值 ```csharp= 回傳值型別 函式名稱(參數型別 參數1, 參數型別 參數2 ...) { 你的程式碼; return 回傳值; } int Answer = 函式名稱(123, "佛光大學"); //執行這個函式 ``` 4. 沒有參數、有回傳值 ```csharp= 回傳值型別 方法(函式)名稱() { 你的程式碼; return 回傳值; } int Answer = 函式名稱(); //執行這個函式 ``` 以上四種形式可以依照你的需要,將需要「打包」的程式給包裝起來, ## 開始修改吧!先找出相似程度高的程式碼 我們先看看之前寫的程式碼。也就是輸入數字按鍵的事件處理,以下是擷取數字鍵3到6的片段程式碼,你有沒有發現,好像都差不多? ```csharp= private void btnThree_Click(object sender, EventArgs e) { if (txtNumber.Text == "0") txtNumber.Text = ""; txtNumber.Text = txtNumber.Text + "3"; } private void btnFour_Click(object sender, EventArgs e) { if (txtNumber.Text == "0") txtNumber.Text = ""; txtNumber.Text = txtNumber.Text + "4"; } private void btnFive_Click(object sender, EventArgs e) { if (txtNumber.Text == "0") txtNumber.Text = ""; txtNumber.Text = txtNumber.Text + "5"; } private void btnSix_Click(object sender, EventArgs e) { if (txtNumber.Text == "0") txtNumber.Text = ""; txtNumber.Text = txtNumber.Text + "6"; } ``` 以上的程式碼,主要都是:「輸入數字按鍵,先檢查輸入文字框是不是0?如果是,先清空,然後再加上數字」,看起來,除了要顯示的數字不同之外,其他程式碼都相近。 :::danger **這些程式碼都太像了,我們最好是把這些程式碼整理起來,最好是只要用一行就可以搞定。** ::: 因此我們可以把這些程式碼打包成一個名稱叫「**Add_Number(增加數字)**」的「函式」。請參考以下程式碼。 ```csharp= private void Add_Number(string _number) { if (txtNumber.Text == "0") txtNumber.Text = ""; txtNumber.Text = txtNumber.Text + _number; } ``` 這個函式是哪一種函式呢?屬於**第二種「有參數、沒有回傳值」的函式**,你可以看到有一個參數,參數就是0~9數字,程式就會依照數字的類別,在輸入文字框中增加數字。 那按鍵事件綁定要怎麼處理呢?請參考以下數字鍵1的作法。 ```csharp= private void btnOne_Click(object sender, EventArgs e) { Add_Number("1"); // 只需要呼叫Add_Number函式,並且設定參數為數字1 } ``` 其他的數字鍵也用類似的作法,請一一完成。這時你再測試程式,就會發現效果是一樣的,而且你應該會覺得,程式碼會比較單純,也比較容易閱讀與修改。 :::success **現在你應該可以體會到,為什麼要寫函式了?** ::: ## 還有什麼地方可以修改? 接下來我們看看四則運算的按鍵,你也可以觀察一下,是不是都很相像呢? ```csharp= private void btnAdd_Click(object sender, EventArgs e) { firstNumber = Convert.ToSingle(txtNumber.Text); //將輸入文字框轉換成浮點數,存入第一個數字的全域變數 txtNumber.Text = "0"; //重新將輸入文字框重新設定為0 operators = 0; //選擇「加」號 } private void btnMinus_Click(object sender, EventArgs e) { firstNumber = Convert.ToSingle(txtNumber.Text); txtNumber.Text = "0"; operators = 1; //選擇「減」號 } private void btnPlus_Click(object sender, EventArgs e) { firstNumber = Convert.ToSingle(txtNumber.Text); txtNumber.Text = "0"; operators = 2; //選擇「乘」號 } private void btnDivide_Click(object sender, EventArgs e) { firstNumber = Convert.ToSingle(txtNumber.Text); txtNumber.Text = "0"; operators = 3; //選擇「除」號 } ``` 看起來也都是很類似的,先將輸入文字框的數字轉型,然後存到全域變數firstNumber中。再將輸入文字框「歸零」,最後將選擇的四則運算代碼存入全域變數operators中。所以我們再設計一個「**Select_Operator (選擇四則運算)**」函式如下: ```csharp= private void Select_Operator(int _operator) { firstNumber = Convert.ToSingle(txtNumber.Text); //將輸入文字框轉換成浮點數,存入第一個數字的全域變數 txtNumber.Text = "0"; //重新將輸入文字框重新設定為0 operators = _operator; //選擇「加」號 } ``` 這個函式設定了一個四則運算類型的參數,其他的程式碼都參考原本的事件處理寫法,只有在第6行的地方,以參數作為四則運算代碼存入全域變數operators中。 原本的事件綁定程式則修改成類似以下的作法,以加法按鍵為例... ```csharp= private void btnAdd_Click(object sender, EventArgs e) { Select_Operator(0); } ...其他你應該會寫了 ``` ## 結語 經過簡單的程式碼優化之後,我們用兩個新增的函式,「**Add_Number(增加數字)**」與「**Select_Operator (選擇四則運算)**」,減少了不少重複性較高的程式碼。 本來是約 175 行的程式碼,調整後應該可以降到約 150 行左右。變化其實不多,但你可以想像一下,假如原來重複性高的程式碼行數很多,如果都改成使用函式,那應該可以更有效的減少程式碼。 再來你應該會覺得,程式碼這樣修改之後會比較有結構化。除了減少程式碼長度外,更重要的是,你要修正或修改程式碼,比較有一定的層次感,不會類似的程式碼到處複製貼上,類似的工作,只要呼叫相對應的「函式」就可以了。 希望你可以初步理解函式的用途與優點! :::spoiler 完整參考程式碼 ```csharp= using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace Calculator { public partial class Form1 : Form { public Form1() { InitializeComponent(); } // 全域變數 float firstNumber, secondNumber; // firstNumber 儲存第一個數字,secondNumber 儲存第二個數字 int operators = -1; // 記錄選擇哪一種運算符號?0:加、1:減、2:乘、3:除、-1:重新設定 private void btnOne_Click(object sender, EventArgs e) { Add_Number("1"); } private void btnTwo_Click(object sender, EventArgs e) { Add_Number("2"); } private void btnThree_Click(object sender, EventArgs e) { Add_Number("3"); } private void btnFour_Click(object sender, EventArgs e) { Add_Number("4"); } private void btnFive_Click(object sender, EventArgs e) { Add_Number("5"); } private void btnSix_Click(object sender, EventArgs e) { Add_Number("6"); } private void btnSeven_Click(object sender, EventArgs e) { Add_Number("7"); } private void btnEight_Click(object sender, EventArgs e) { Add_Number("8"); } private void btnNine_Click(object sender, EventArgs e) { Add_Number("9"); } private void btnZero_Click(object sender, EventArgs e) { Add_Number("0"); } private void Add_Number(string _number) { if (txtNumber.Text == "0") txtNumber.Text = ""; txtNumber.Text = txtNumber.Text + _number; } private void btnAdd_Click(object sender, EventArgs e) { Select_Operator(0); } private void btnMinus_Click(object sender, EventArgs e) { Select_Operator(1); } private void btnPlus_Click(object sender, EventArgs e) { Select_Operator(2); } private void btnDivide_Click(object sender, EventArgs e) { Select_Operator(3); } private void Select_Operator(int _operator) { firstNumber = Convert.ToSingle(txtNumber.Text); //將輸入文字框轉換成浮點數,存入第一個數字的全域變數 txtNumber.Text = "0"; //重新將輸入文字框重新設定為0 operators = _operator; //選擇「加」號 } private void btnDot_Click(object sender, EventArgs e) { // 確認輸入文字框中完全沒有小數點 if (txtNumber.Text.IndexOf(".") == -1) txtNumber.Text = txtNumber.Text + "."; } private void btnClear_Click(object sender, EventArgs e) { txtNumber.Text = "0"; firstNumber = 0f; secondNumber = 0f; operators = -1; } private void btnEqual_Click(object sender, EventArgs e) { float finalResults = 0f; //宣告最後計算結果變數 secondNumber = Convert.ToSingle(txtNumber.Text); //將輸入文字框轉換成浮點數,存入第二個數字的全域變數 //依照四則運算符號的選擇,進行加減乘除 switch (operators) { case 0: finalResults = firstNumber + secondNumber; break; case 1: finalResults = firstNumber - secondNumber; break; case 2: finalResults = firstNumber * secondNumber; break; case 3: finalResults = firstNumber / secondNumber; break; } txtNumber.Text = string.Format("{0:0.##########}", finalResults); //在輸入文字框中,顯示最後計算結果,並且轉換成格式化的字串內容 //重置所有全域變數 firstNumber = 0f; secondNumber = 0f; operators = -1; } } } ``` :::