ｃ＃で電卓を作っていく(2)

## 挨拶
皆さんこんにちは前回はGUIの制作だけで記事が終わってしまったので今回はごりごりコードを書いていきたいと思います。

__前回の記事はこちら__

https://qiita.com/yuulyy/items/19604dfbe4e3b39c5532


## 電卓の内部のコードを作っていく!

### 簡単な設計
今回の電卓を作るにあたって必要そうな処理は

1. 数字のボタンを押すとLabelにそれが表示される。（桁の制限があることを忘れずに）
2. 演算子のボタンを押すと押す前に入力していた数字を上の段に移し下の段のラベルの文字列を消去、そして演算子を置く場所に演算子を表示
3. 演算子を押した後別の演算子のボタンを押した場合演算子のみ変更されるようにするプログラム
4. +と-を入れ替えるボタンを押したときラベルにある+と-を入れ替える
5. ACボタンは上段下段すべて消去、Cボタンは上段以外の情報を初期化する
6. "＝"ボタンを押したときに計算結果を上段のラベルに表示する
7. 割り算を使用する時decimalを使う、そして0で割れないよう処理をする

ざっと思い浮かぶ処理はこんな感じでしょうか？
それでは上記で上げたものを実現するためにコードを書いていきたいと思います。

#### フィールドの定義
最初に必要なものをフィールドとして定義しておきます。
```C#:ButtonClick.cv
    private const int MAX_LENGGTH = 8; //打ち込める最大文字数
    private string error = String.Format("{0}桁を超えてしまいました",MAX_LENGGTH.ToString());
    private string formula = ""; //式を入れる為の変数
    private string term = ""; //計算記号を押す前の数列
    private string mark = ""; //計算記号置き場
    private string sigin = "+"; //+、-を格納するフィールド＊初期値は+
```

#### +と-を切り替える処理
先ず初めにこの **+と-を切り替える処理** を作っていきたいと思います。
＊前提として+と-を置く場所として定義したLabelのNameはPMLabelとします。
```C#:ButtonClick.cv
        private void ButtonClick(object sender, EventArgs e)
        {
            switch (sigin)
            {
                case "+":
                    sigin = "-";
                    break;
                case "-":
                    sigin = "+";
                    break;
            }
            PMLable.Text = sigin;
        }
```
コードはこんな感じ。

sigin変数に入っている文字列によって処理を分け、それをLabelのTextに入れるというコードです。
イベントを作成したらボタンに紐づけるのを設定を忘れずに！

#### ボタンを押した時にLabelに反映させるための処理
次に作成していくのは __ボタンを押した時Labelに反映させるための処理__ です
＊前提として反映させるLabelのNameはbottomLabelとします。

```C#:ButtonClick.cv
        private void numButtonClick(object sender, EventArgs e)
        {
            if (sender is Button button)
            {
                if (formula.Length < MAX_LENGGTH)
                {
                    formula += button.Text;
                    bottomLabel.Text = formula;
                }
            }
        }
```
このコードではこのイベントを呼び出したのがButtonオブジェクトだということを確認し桁数が超えていなければformula変数へ格納しbottomLabelに表示するという処理になっています。

#### 演算子ボタンを押したときの処理
次は __演算子ボタンを押したときの処理__ です。
＊前提
＊演算子を置いておくLabelのNameはoperationSymbol
＊上段のLabelのNameをtopLabelとします。
```C#:ButtonClick.cv
        private void markButtonClick(object sender, EventArgs e)
        {
            if (sender is Button button)
            {
                if (formula.Length != 0 && mark == "")
                { //数字が入力されているかつ演算子が入力されていない場合の処理
                    mark = button.Text;
                    operationSymbol.Text = mark;
                    //数列を上の段に移し下の段の数式は初期化する
                    switch (sigin)
                    { //符号によって処理を変える
                        case "+": // +の場合はそのまま上に移す
                            term = formula;
                            break;
                        case "-": // -の場合は数列の前に-記号をつけ上に移す
                            term = sigin + formula;
                            break;
                    }
                    topLabel.Text = term;
                    formula = "";
                    bottomLabel.Text = formula;
                }
                else if (mark != "")
                { //演算子がもう入力されている場合演算子のみ変更する処理
                    mark = button.Text;
                    operationSymbol.Text = mark;
                }
            }
        }
```

コードはこんな感じ。

数字が何も入力されておらず演算子も入力されていない（初期状態の場合）なにも反応しないようになっており。
一項目を入力し演算子ボタンを押すと一項目が上段のLabelに移動し下段のLabelは初期化されます（この時に正負の符号も反映させます）。
二項目を入力中に演算子を切り替えたくなった場合の処理をelse ifとして記述しています

#### クリアとオールクリアの処理
```C#:ButtonClick.cv
        private void allClear(object sender, EventArgs e)
        {
            //下段初期化
            formula = "";
            bottomLabel.Text = "";
            //上段初期化
            term = "";
            topLabel.Text = "";
            //演算子初期化
            mark = "";
            operationSymbol.Text = "";
            //符号初期化
            sigin = "+";
            PMLabel.Text = sigin;
        }

        private void clear(object sender, EventArgs e)
        {
            //下段のLabelの初期化
            formula = "";
            bottomLabel.Text = "";
            //演算子初期化
            mark = "";
            operationSymbol.Text = "";
            //符号の初期化
            sigin = "+";
            PMLabel.Text = sigin;
        }
```

コードはこんな感じ

clearメソッドで下段のLabel、演算子を置くためのLabel、正負の符号を置くためのLabelを初期化。
allClearメソッドはclearメソッドに上段のLabelの初期化が追加されています。

#### 計算の処理
```C#:ButtonClick
private void calculator(object sender, EventArgs e)
{
    if (mark != "" && formula != "")
    {
        string ans = "";
        switch (mark)
        {
            case "+":
            case "-":
            case "×":
                int num1 = int.Parse(term);
                int num2 = int.Parse(formula);
                switch (mark)
                {
                    case "+":
                        ans = (num1 + num2).ToString();
                        break;
                    case "-":
                        ans = (num1 - num2).ToString();
                        break;
                    case "×":
                        ans = (num1 * num2).ToString();
                        break;
                }
                if (ans.Length > MAX_LENGGTH)
                {
                    ans = error; // 最大桁数を超えてしまった場合の処理
                }
                break;
            case "÷":
                if (int.Parse(formula) == 0)
                { //0では割れないのでボタンが押されても何も起こらなくする
                    return;
                }
                ans = (decimal.Parse(term) / decimal.Parse(formula)).ToString();
                Number.Text = ans;
                if (ans.IndexOf(".") >= 0)
                { //答えに小数点が含まれる場合の処理
                    if (ans.Substring(0, ans.IndexOf(".")).Length > MAX_LENGGTH)
                    {
                        //整数の部分のみで有効桁数を超えてしまった場合の処理
                        ans = error;
                    }
                    else if (ans.Substring(0, ans.IndexOf(".")).Length == MAX_LENGGTH - 1)
                    { //整数部分+小数点でMAX_LENGTHになってしまった場合の処理
                        ans = ans.Substring(0, ans.IndexOf("."));
                    }
                    else if (ans.Length > MAX_LENGGTH)
                    {
                        ans = ans.Substring(0, MAX_LENGGTH);
                    }
                }
                else
                { //応えに小数点が含まれていない場合の処理
                    if (ans.Length > MAX_LENGGTH)
                    {
                        ans = error;
                    }
                }
                break;
        }
        allClear(new object(), new EventArgs());
        topLabel.Text = ans;
    }
}
```
コードはこんな感じ。

コードの流れとしては計算結果をansという変数に格納しそれを上段のLabelに表示するというもの。
##### 細かい説明
+、-、×は一度intに直してから計算しans変数に文字列として格納。もしも有効桁数を超えたら定数のエラー文を出力するように変数に格納します。
÷の場合はcase句のすぐ後に0出ないかの確認を挟んでいます。もし0だった場合は何もせずにメソッドが終わります。
0ではなかった場合decimal型に変換しなるべく正確な値の取得を目指しました。小数点を含んだ状態で有効桁数の文字列としたかったためif文の記述が長くなってしまいました…

最後に全てのLabelを初期化するためallClearメソッドを呼び出します。allClearメソッドでは引数に渡された値を使っていないので適当なオブジェクトを使用しメソッドを呼び出しています。

そして最後に上段のLabelに計算結果を表示して終了です。

## 完成
制作時間訳5時間ぐらい？
最初の要件に書いたものはここまでのコードで全て実現できたと思います。

## 作ってみての感想
私は元々Javaを少し触っていたのでC#の文法はすごく理解しやすく非常に助かりました。
そして一つのアプリケーションを作ることを通してC#の理解も少し深まりました。
これからも様々なプログラムと出会って成長していきたいと思います。
それでは今回はこの辺にしようと思います、最後まで読んでいただきありがとうございました。