---
# System prepended metadata

title: 單位轉換程式：建立函式（方法）
tags: [視窗程式設計]

---

---
tags: 視窗程式設計
---

# 單位轉換程式：建立函式（方法）

接下來我們幫同學建立「函式」的概念，還記得前述的說明嗎？

:::info
1. 你需要重複貼上好多程式碼
2. 因為每一個單位轉換都需要仔細的改程式，如果要測試，很麻煩
3. 如果有錯，還要把所有程式都找過一次，很痛苦
:::

你可以檢視目前的程式，你一定會發現：**==很多地方看起來都很像==**。

我們有沒有辦法把一些看起來很類似的程式，通通打包起來，寫在某個地方，然後如果我要使用的時候再來「呼叫」這段打包好的程式？就不用一直重複複製貼上這些類似的程式。

當然可以，這就是「**函式（方法）**」最重要的用途！簡單來說，**函式就是把常用的程式碼打包裝在一個區塊中，方便我們在寫程式時隨時呼叫、使用**。

同學可以先參考下圖：

![](https://i.imgur.com/QmBnp8o.png)

先看左側，程式最基本的執行流程就是一步一步往下執行，依照步驟完成工作，如果某段程式是會重複在不同階段執行，你可能需要不斷的重複複製貼相同的程式碼。程式會變的又臭又長，難以閱讀，又難以維護。

所以我們**可以將一大段程式打包起來，需要的時候才呼叫，只需要一行就能執行這一大段程式碼**（請看右側），而且打包起來的程式有一定的獨立性，當你需要除錯、修改，只需要修改函式就好。

:::info
適度的將重複性高的程式「打包」成函式（方法），能讓程式看起來更簡潔！
:::

## 函式的觀念可以想成販賣機

![](https://i.imgur.com/glcO2dp.jpg =500x)

**你一定用過販賣機，投幣之後，按照你的選擇（輸入），他會給你不同的商品（結果）。**

函式也是類似的概念，你設計好一個函式，可以給予不同的輸入值，經過函式內的程式運算，得到值。

以下是一個販賣機的「想像函式」：

```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);
// 販賣機不會有飲料出來
```

## 函式 Function 的介紹

當程式越來越複雜的時候，就必須將一些重複、或者需要獨立處理，有特別用途的程式，拆開整理成容易管理的程式片段，這些小程式就稱為「**函式**」。

:::info
函式是一種有名稱(類似變數)且獨立的程式片段，可以接收任何型態的參數，處理完成後也可以輸出任何型態的結果。
:::

最簡單的函式可以是這個樣子的：

:::success
假設我們要計算「兩個數值相加」，所以我們設計一個函式，我們名稱就稱為「Add」，並且這個函式可以輸入任何兩個數值，然後可以回傳結果。
:::

```csharp=
// 存取修飾詞 回傳數值的資料型態 函数名稱(參數列表)
public int Add(int a, int b)
{   
    int sum = a + b;  // 函式裡面的程式，通常要縮排
    
    return sum; // 函式的程式要回傳出來的結果
}

// 使用函式
int result = Add(5, 3); // 執行 Add 函式，並且輸入 5 和 3
Console.WriteLine(result); // 函式傳出來的結果：8
```

程式碼簡單說明如下：

:::danger
第2至7行：

1. 定義了一個名為 Add 的函式，該函式接收兩個整數型態的參數 a 和 b，並且回傳一個整數值。
2. 在函式內部，將參數 a 與 b 相加，並將結果儲存在一個稱為 sum 的整數型態變數中。
3. 通過 return 關鍵字，將計算結果 sum 回傳給呼叫該函式的程式碼。

第10與11行則是使用這個函式：

1. 如果要執行這個「Add函式」，你只要把 Add 打出來，用括號把兩個整數值 (5和3) 包起來，然後用逗點隔開 5 和 3。
2. 5 + 3 的結果會儲存在 result 變數中，然後使用 Console.WriteLine 將結果輸出到控制台上。
3. 由於我們傳入的是 5 和 3，所以 Add 函式會計算它們的和，結果是 8。
:::

## 函式的撰寫要注意的地方

* **在函式內部要執行的程式碼要用一組大括號 { } 包裹**

你可以仔細觀察之前寫的範例函式，有沒有注意到兩個數值 a 與 b 相加的程式碼，被包裹在一組大括號裡面？

:::success
通常，我們會把一組大括號的左右符號，都用一行來寫，程式碼則是縮排，目的是這樣比較好閱讀。
:::

* **回傳值的資料形態**

函式有其回傳值。一個函式在定義時，就要先定義其回傳值的資料形態，此項寫法如：int、string、double、或是類別名。若不將此函式設回傳值，則此項須寫為void。若回傳值的資料形態不為void，表示函式一定要寫回傳值。反之，若回傳值的資料形態為void，表示函式就不能寫回傳值。

* **參數列表**

通常大概寫成用括號包裹所有的參數列表，例如：*(參數型別 參數1, 參數型別 參數2 ...)*

用來設定該函式的所有參數，參數的數目可以任意多個，也可以完全都沒有，參數是由函式外部輸入，並在函式內部的程式碼產生作用。

### 實際上也很重要，但以下可以先跳過

* **存取修飾詞：可寫可不寫**。共有三種寫法：

1. public：代表此函式能夠讓程式中所有類別存取。
2. protected：代表此函式只能夠讓其所屬的類別、及這個類別的子類別存取。
3. private：代表此函式只能夠讓其所屬的類別存取。
4. 若不寫，則預設為private。

* **static：可寫可不寫**
 
若有寫，則此函式就是「靜態函式」，靜態函式只能對其所屬類別中的靜態變數做存取，而不能存取非靜態變數。若不寫，則此函式就是「非靜態函式」，非靜態函式則可以存取靜態變數與非靜態變數兩者。


## 函式的類型

無論是哪一種程式語言，函式都可以寫成四種型式，以下是 C# 中的寫法：

1. **沒有參數也沒有回傳值**
```csharp=
void 函式名稱() { 
    你的程式碼;
} 

函式名稱(); //執行這個函式
```
如果你看到你的方法有**void，代表沒有回傳值** 

2. **有參數、沒有回傳值**
```csharp=
void 函式名稱(參數型別 參數1, 參數型別 參數2 ...) { 
    你的程式碼; 
} 

函式名稱(123, "佛光大學"); //執行這個函式
```

3. **有參數、有回傳值** 
```csharp=
回傳值型別 函式名稱(參數型別 參數1, 參數型別 參數2 ...) { 
    你的程式碼;
    return 回傳值;
} 

int Answer = 函式名稱(123, "佛光大學"); //執行這個函式
```

4. **沒有參數、有回傳值**
```csharp=
回傳值型別 方法(函式)名稱() { 
    你的程式碼;
    return 回傳值;
}  

int Answer = 函式名稱(); //執行這個函式
```

以上四種形式可以依照你的需要，將需要「打包」的程式給包裝起來。

## 開始修改吧！

以下程式碼則是要用在我們的長度計算機裡面，我們先介紹怎麼使用函式？再介紹怎麼建立函式？

## 怎麼使用函式？

我們先挑選「公分輸入文字框」，修改原有的事件綁定程式片段，建議同學要跟之前的版本做一下比較，你會更理解為什麼要這樣修改？你也可以發現，其實只是把單位轉換計算的部分刪除（這邊老師暫時先把它註解，讓你可以做比較），然後加上一段「執行」計算長度函式的語法。

```csharp=
private void txtCM_KeyUp(object sender, EventArgs e)
{
    strInput = txtCM.Text;

    if (double.TryParse(strInput, out douOutput) == true)
    {
        //我們把以下的單位轉換，改成可以共用的函式
        //txtM.Text = string.Format("{0:0.##########}", douOutput / 100);
        //txtKM.Text = string.Format("{0:0.##########}", douOutput / 100000);
        //txtIn.Text = string.Format("{0:0.##########}", douOutput / 2.54);
        //txtFt.Text = string.Format("{0:0.##########}", douOutput / 30.48);
        //txtYard.Text = string.Format("{0:0.##########}", douOutput / 91.44);
        
        //執行計算長度函式
        caculateAnswer(0, douOutput); 
    }
    else
    {
        txtInfo.Text = "請輸入數字";
        txtCM.Text = "";
    }
}
```

你可以看的到，原本需要五行的單位轉換計算，就變成只要一行就可以完成。

```csharp
caculateAnswer(0, douOutput); 
```

:::info
請特別注意：呼叫函式的方法需要指定「函式名稱」，有參數的話要放在括號裡面，參數用逗點隔開。
:::

## 怎麼建立函式？

現在你應該已經初步理解為什麼函式可以大幅度減少程式碼了？接下來要教同學怎麼建立函式？

以下是原來的單位轉換計算程式碼，我們要將它「打包」起來。

```csharp=
// 我們把這一段，改成可以共用的函式
txtM.Text = string.Format("{0:0.##########}", douOutput / 100);
txtKM.Text = string.Format("{0:0.##########}", douOutput / 100000);
txtIn.Text = string.Format("{0:0.##########}", douOutput / 2.54);
txtFt.Text = string.Format("{0:0.##########}", douOutput / 30.48);
txtYard.Text = string.Format("{0:0.##########}", douOutput / 91.44);
```

打包的結果如同以下程式碼，我們設計一個單位轉換計算的函式，沒有回傳值（void），設計兩個參數，1.類別參數（_kind）、2.數值參數（_value）。

```csharp=
// 設計一個單位轉換計算的函式，沒有回傳值，設計兩個參數，1.類別參數、2.數值參數
private void caculateAnswer(int _kind, double _value)
{
    if (_kind != 0)
        txtCM.Text = string.Format("{0:0.##########}", _value);
    if (_kind != 1)
        txtM.Text = string.Format("{0:0.##########}", _value / 100);
    if (_kind != 2)
        txtKM.Text = string.Format("{0:0.##########}", _value / 100000);
    if (_kind != 3)
        txtIn.Text = string.Format("{0:0.##########}", _value / 2.54);
    if (_kind != 4)
        txtFt.Text = string.Format("{0:0.##########}", _value / 30.48);
    if (_kind != 5)
        txtYard.Text = string.Format("{0:0.##########}", _value / 91.44);
}
```

## 單位轉換計算函式

以上的單位轉換計算函式有幾個重點：

**類別參數**：包括0到1的數字，用來代表照使用者選擇的長度單位類型，我們的範例程式將每個長度類別設定為：

1. 公分輸入文字框：0
2. 公尺輸入文字框：1
3. 公里輸入文字框：2
4. 英吋輸入文字框：3
5. 英呎輸入文字框：4
6. 英碼輸入文字框：5

**數值參數**：使用者在選擇的單位中，所輸入的數字。例如：如果使用者選擇在公分輸入文字框輸入100，類別參數就是0，數值參數為100。

**計算方式**：你可以觀察我們設計的單位轉換計算函式，都是以「公分」為基礎來做計算。以下我們說明這個函式程式運作的流程，以使用者選擇在公分輸入文字框為案例說明：

使用者選擇在公分輸入文字框，所以「_kind=0」，輸入「_value=100」的數值，接下來程式會一口氣跑好幾個判斷式，除了第4、5行程式沒有跑，其他都會一一計算並且在每一個輸入文字框顯示結果。

那如果使用者選擇其他長度單位的輸入文字框，那又會怎麼計算呢？這邊我們來看看「公尺的綁定事件程式碼片段」。

```csharp=
// 公尺的綁定事件程式碼片段
private void txtM_KeyUp(object sender, KeyEventArgs e)
{
    strInput = txtM.Text;

    if (double.TryParse(strInput, out douOutput) == true)
    {
        caculateAnswer(1, douOutput * 100); // 事先將公尺轉換成公分
    }
    else
    {
        txtInfo.Text = "請輸入數字";
        txtM.Text = "";
    }
}
```

你可以看的到，如果使用者選擇公尺的輸入文字框，會事先將公尺轉換成公分，然後丟到單位轉換計算的函式做計算，因為函式內部的單位轉換是將「公分數值轉為其他長度單位」。

所以不管有多少長度單位，只要能事先轉換成公分，就可以處理各式各樣的長度單位，這樣程式就會更簡單也更容易修改。你可以再研究一下其他單位，你應該發現原理都是一樣的。

## 結語

這個章節我們初步介紹了 C# 的函式寫法。

**函式是模組化程式設計的基石**，除了函式可以在程式中重複使用，我們還可以將各種不同的功能分別寫成不同功能的函式，可以將不同功能函式組合，組合出完整的系統或軟體功能。

這樣就可以讓你不會寫出「落落長」程式碼，可以適當的整合重複程式碼，還可以簡化程式設計的複雜程度，讓你寫出更清楚更好修改的程式碼。

之後我們會在之後的章節介紹類別與物件的概念，你可以更感受到函式的優點，並且函式也是類別的基本元素，因此了解函式，等於你更深入了解程式設計的世界。
