---
tags: 視窗程式設計
---
# 計算機程式:實現基本四則運算
我們已經初步完成基本計算機的介面,然後也能讓它實現按下數字鍵,出現數字的效果。
接下來我們讓計算機可以選擇運算符號,再輸入第二個數字,最後按下等於按鍵,完成計算。
## 複習一下
:::success
你可以拿出你手機裡面的計算機App,一邊操作一邊想想,你是怎麼使用計算機的?
:::
我們再回想一下,計算機是怎麼操作的?因為這會決定我們要怎麼寫程式?
之前的練習,應該已經完成第一個項目,現在我們要完成2-4的項目。
:::info
1. ~~輸入第一個數字:輸入一個數字,從最左邊的數字輸入到最後一個數字~~ **(這個已經完成了)**
2. 選擇四則運算符號:從加、減、乘、除中挑選一個
3. 輸入第二個數字::輸入一個數字,從最左邊的數字輸入到最後一個數字
4. 按等於按鍵:得到計算結果
:::
``` mermaid
graph TD;
按下數字按鍵輸入第一個數字-->選擇要運算的四則運算符號;
選擇要運算的四則運算符號-->按下數字按鍵輸入第二個數字;
按下數字按鍵輸入第二個數字-->按下等於按鍵;
按下等於按鍵-->顯示計算結果;
```
## 使用全域變數,紀錄輸入的第一個數字、第二個數字、選擇的四則運算符號
我們先宣告全域變數,**==順便考考你,全域變數要放在什麼地方?==**
```csharp=
// 全域變數
float firstNumber, secondNumber; // firstNumber 儲存第一個數字,secondNumber 儲存第二個數字
int operators = -1; // 記錄選擇哪一種運算符號?0:加、1:減、2:乘、3:除、-1:重新設定
```
還記得所謂的「**全域變數**」嗎?這邊我們利用三個全域變數,來記錄「**第一個數字 firstNumber**」、「**第二個數字 secondNumber**」、「**選擇的四則運算符號 operators**」,最後三個全域變數將會組合在一起,計算結果。
:::info
如果你忘了什麼是全域變數,請點選 [**這裡**](https://hackmd.io/@chohsunlu/ByHy31u2t/%2FC9WuZRdnTG6OlnT8U3G5Yw#%E5%85%A8%E5%9F%9F%E8%AE%8A%E6%95%B8%E7%9A%84%E8%A7%80%E5%BF%B5)。
:::
設計這三個全域變數,是為了依照前述的程式邏輯。
**我們將第一個輸入的數字,先存到 firstNumber 變數中,然後再用 operators 記錄按了哪一個四則運算符號?再依樣畫葫蘆,輸入第二個數字,存到 secondNumber 中。**
為什麼要這麼做?我們先完成四則運算按鍵的事件綁定,再來解釋為什麼要這樣設計?
<!-- 最後,按下「等於」按鍵,就依照選擇的四則運算符號,把第一個與第二個數字進行計算,最後顯示在輸入文字框中,呈現計算結果。 -->
## 設定四則運算按鍵的事件綁定
剛剛我們已經說明了,我們將第一個輸入的數字,先存到 firstNumber 變數中,然後再用 operators 記錄按了哪一個四則運算符號?再依樣畫葫蘆,輸入第二個數字,存到 secondNumber 中。
那我們要在什麼時候「記錄」第一個數字呢?
:::info
答案:在「**按下四則運算按鍵**」的時候。
:::
你可以回想一下你怎麼使用計算機?當你確定第一個數字的內容,接下來按的按鍵,就是「四則運算按鍵」,可能是加減乘除的其中一個,所以在這個階段紀錄第一個數字,應該是一個合適的時機。
此外大多數的計算機,按下「四則運算按鍵」後,也同時將輸入文字框重新設定為 0 ,並且也同時記錄選了哪一種四則運算的符號。
因此請同學以下參考的程式碼,請試著做好這四個按鍵的事件綁定,再將程式加入。你也可以仔細看一下,每一行程式在做甚麼?應該都符合以上的設計。
然後可以試著測試程式看看,應該輸入第一個數字之後,選擇任一個四則運算符號,就會同時儲存第一個數字,也儲存了選擇的運算符號,同時,輸入文字框也會重設為 0。
```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**」,因為這四個區域都要控制這兩個變數的內容,所以你必須要將這兩個變數設定為全域變數。
## 輸入第二個數字
這時候你也會發現,再直接輸入數字,一樣也可以輸入,所以這部分也OK了,不需要寫額外的程式碼。
然後你可以想想看,那麼**什麼時候可以儲存二個數字呢?**
:::info
答案:在「**按下等於按鍵**」的時候。
:::
## 設定等於按鍵的事件綁定
由於按下等於按鍵,相當於要完成計算過程,因此,我們在等於按鍵裡面,來儲存第二個輸入的數字,並且完成最後計算。
請將等於按鍵的事件綁定做好,再將以下程式加入。
```csharp=
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;
}
```
這裡稍微說明一下程式的內容。
:::success
* 第3行:宣告一個用來儲存「**最後計算結果 finalResults**」的變數,用來儲存最終的計算結果。你應該會發現,這個變數是所謂的「區域變數」,因為這個變數只有在這個等於按鍵事件綁定才有作用,而且我們也只需要在這裡有作用就好,不必做成全域變數。
* 第4行:將第二個數字存到「**第二個數字 secondNumber**」。
* 第7-21行:使用一個 **switch case 的條件判斷式**,依照當初選擇的四則運算符號,將第一個數字和第二個數字進行加減乘除的計算,最後存到「**最後計算結果 finalResults**」。
* 第23行:把「**最後計算結果 finalResults**」變數的資料,將它轉型成為格式化的字串,顯示在輸入文字框中,完成計算!
* 第26-28行:最後一小段重置所有全域變數,通通重新設定為初始值,這樣可以讓我們重新再做計算。
:::
可能有的同學不熟悉甚麼是 switch case 條件判斷式?以下我們繼續說明。
## switch case 條件判斷
switch case 可用來「連續」比較數值或字元,讓你能夠一次將多個條件一同進行判斷,語法架構如下:
```csharp=
switch(變數名稱或運算式) {
case 符合數字或字元:
陳述句一;
break;
case 符合數字或字元:
陳述句二;
break;
default:
陳述三;
break;
}
```
首先看看 switch 的括號,當中置放要判斷的變數或運算式,會與 case 設定的數字或字元比對,如果符合就執行 case 以下的陳述句,直到遇到 break 後離開 switch 區塊,若沒有符合的數值或字元,會執行 default 後的陳述句,如果你沒有預設項目,default 可以省略。
其實 switch 你可以看成一個「**==連續的 if 判斷式==**」,我們可以用以上的範例來說明。
原來是:
```csharp=
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;
}
```
如果使用 if 判斷式:
```csharp=
if (operators == 0)
finalResults = firstNumber + secondNumber;
if (operators == 1)
finalResults = firstNumber - secondNumber;
if (operators == 2)
finalResults = firstNumber * secondNumber;
if (operators == 3)
finalResults = firstNumber / secondNumber;
```
上面和下面基本上都是一樣的結果(不信的話你可以丟進去試試看),那麼哪一種比較好呢?這要看情形,如果你的多條件判斷很單純,例如我們的案例,只要看是哪一種數值的情境,switch case 看來就挺好用的。但如果你的多條件判斷比較複雜,又需要在每一種判斷結果進行其他的判斷,可能還是一般的 if else 比較合適。
隨著程式的複雜度提高,有些程式設計師反倒認為 switch case 過時了,有的程式語言甚至沒有 switch case 語法,例如 Python 就沒有,你要做類似的效果還是只能寫 if else。
## 小數點的輸入
還有一個東西沒有完成,就是小數點按鍵的處理,你可以回去使用計算機試試看,小數點按鍵的特點是「如果輸入文字框已經有小數點了,就不能再輸入小數點」。所以必須要先判斷有沒有小數點,才能讓程式在數字後面加上小數點。
```csharp=
private void btnDot_Click(object sender, EventArgs e)
{
// 確認輸入文字框中完全沒有小數點
if (txtNumber.Text.IndexOf(".") == -1)
txtNumber.Text = txtNumber.Text + ".";
}
```
我們怎麼實現檢查有沒有小數點?在 C# 中,我們可以使用「**==IndexOf()==**」方法,他可以用來顯示要找尋的字串首次出現的從零開始的位置。如果找不到這個字串,則該方法回傳「-1」。
以下是IndexOf() 方法的語法:
```csharp=
要搜索的原始字串.IndexOf(要尋找的字串, 開始搜索的位置, 搜索結束的位置)
```
其中「開始搜索的位置」與「搜索結束的位置」可以省略,就會搜尋整個字串內容。
因此以上的程式片段意思就是:我們要找尋的是小數點字元「.」,並且判斷有沒有等於「-1」。
如果是「-1」,代表就是沒有小數點,這時候才在輸入的數字後面加上小數點。
如果不是「-1」,也就是有小數點,就不增加小數點,避免連續出現小數點。
## 清除按鍵的事件綁定
最後,我們把清除按鍵完成,清除按鍵相當於重新設定所有全域變數,然後將輸入文字框重置顯示為「0」,請參考以下程式碼。
```csharp=
private void btnClear_Click(object sender, EventArgs e)
{
txtNumber.Text = "0";
firstNumber = 0f;
secondNumber = 0f;
operators = -1;
}
```
## 結語
到目前為止,我們已經初步完成這個小型計算機的所有功能,以下是完整的參考原始碼。
當然,這不是最後答案,我們還是要針對它做點小修改,讓整個程式更簡潔一點。
```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)
{
if (txtNumber.Text == "0")
{
txtNumber.Text = "";
}
txtNumber.Text = txtNumber.Text + "1";
// txtNumber.Text += "1"; // 上面和下面的寫法意思是一樣的
}
private void btnTwo_Click(object sender, EventArgs e)
{
if (txtNumber.Text == "0") // 如果你的判斷式簡單到只有一行程式,可以把 { } 大刮號省略掉
txtNumber.Text = "";
txtNumber.Text = txtNumber.Text + "2";
}
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";
}
private void btnSeven_Click(object sender, EventArgs e)
{
if (txtNumber.Text == "0")
txtNumber.Text = "";
txtNumber.Text = txtNumber.Text + "7";
}
private void btnEight_Click(object sender, EventArgs e)
{
if (txtNumber.Text == "0")
txtNumber.Text = "";
txtNumber.Text = txtNumber.Text + "8";
}
private void btnNine_Click(object sender, EventArgs e)
{
if (txtNumber.Text == "0")
txtNumber.Text = "";
txtNumber.Text = txtNumber.Text + "9";
}
private void btnZero_Click(object sender, EventArgs e)
{
if (txtNumber.Text == "0")
txtNumber.Text = txtNumber.Text + "0";
}
// 按下選擇「加」按鍵
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; //選擇「除」號
}
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;
}
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;
}
}
}
```