---
# System prepended metadata

title: 計算機程式：優化程式碼
tags: [110-2視窗程式設計]

---

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