---
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;
}
}
}
```
:::