【C# 01】プログラミングを知らないインフラエンジニアが C# の勉強を始めてみた。(電卓アプリ作成)

#はじめに

私はとある会社でAzure のサービスの中の 1 つである、 Azure Active Directory に関するサポート エンジニアの仕事をしております。
Azure Active Directory はクラウド サービスの認証基盤をつかさどっているので、当然、クラウド上のアプリケーションもしくはオンプレミス環境上にあるアプリケーションをインターネット上に公開し、Azure AD を使って認可、認証させたいというご要望をいただきます。

認証認可のプロトコルの動きや仕様を押さえておけば、お客様のご要望に応えられることも可能です。
しかしながら、アプリケーション側の動きも押さえられればより良い回答を返答できるようになることは言うまでもありません。
(もちろんアプリケーションに精通しているエンジニアがおり、チームでサポートしているので必ずしも 1 人ですべてを解決する必要はありません)

とはいえ、インフラ エンジニアとアプリケーション エンジニアの境目が無くなってきつつある現代において、インフラ エンジニアもプログラミングができるようになるに越したことはありません。

そこで私は、先日投稿した下記記事にもありますように、プログラミングに精通した、あるエンジニアの方にメンターになっていただき、C# を中心に月に 1 回ほどのペースでプログラミングの成果物を出すことにしました。

-参考情報
エンジニアとして成長するために皆さんにもやって欲しいこと。
URL:https://qiita.com/Shinya-Yamaguchi/items/73d0e0c8cd6d8b21070f

このシリーズは、とあるインフラ エンジニアが C# を使ってプログラミングができるようになるまでの軌跡を綴ります。

#筆者スペック

筆者のプログラミングに関するスキルは以下のとおりです。

・Linux の bash によるシェルスクリプトの経験はあり
・プログラミングに関する知識はほぼ皆無
・プログラミングに関する書籍は何冊か買って手を動かしたことはあるが、いずれも途中で挫折

いままで挫折していた原因は、具体的な目標を立てていなかったからだと思います。
従って上述のとおり、月単位で目標を決めて、期限を設けてアウトプットすることにしました。

#やってみる
まず第一歩として、Microsoft が公開している Microsoft Docs (URL:https://docs.microsoft.com/ja-jp/?WT.mc_id=docs-twitter-machiy) の中で、 C# を使った基本的なコンソール アプリの作成に挑戦してみました。

-参考情報
チュートリアル: Visual Studio でシンプルな C# コンソール アプリを作成する
URL:https://docs.microsoft.com/ja-jp/visualstudio/get-started/csharp/tutorial-console?view=vs-2019

チュートリアルを見ていただければ分かりますが、C# のコード サンプルが記載されているので、そのままサンプル コードを貼り付けてしまえば、本チュートリアルの目的である電卓アプリは完成してしまいます。
ただそれでは当然ながら何の学びもないので、実際には、すべて手入力しています。
また、前提として、既に Visual Studio 2019 の無料版 (URL:https://visualstudio.microsoft.com/ja/downloads/?rr=https%3A%2F%2Fdocs.microsoft.com%2Fja-jp%2Fvisualstudio%2Fget-started%2Fcsharp%2Ftutorial-console%3Fview%3Dvs-2019) をダウンロードしています。

「スタート」→「Visual Studio 2019」の順にクリックし、右下の「新しいプロジェクトの作成」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/f279addd-522f-ab04-4f91-ebc05adcd9bd.png)

コンソール アプリ(.NET Core) を選択された状態で「次へ」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/2d15f2a4-265b-d64c-346d-af61d8637f49.png)

プロジェクト名に「Calculator」を入力し「作成」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/fb99a709-9fee-255d-5186-794084807f4e.png)

作成されたプロジェクトのデフォルトの状態が下記となります。
Console.WriteLine("Hello World!"); の意味は、画面上に、「Hello World!」と 1 行表示することを意味します。

```c#

using System;

namespace Calculator
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}
```

上記 Hello World! の代わりに、下記のとおり整数の計算をするコードを記入してみます。


```c#

using System;

namespace Calculator
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 42;
            int b = 119;
            int c = a + b;
            Console.WriteLine(c);
            Console.ReadKey();
        }
    }
}
```

修正後に「F5」ボタンもしくは画面上部の「Calculator」をクリックすることで、コンソール ウィンドウが開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/7d5c46b5-1bf8-3b7f-9085-ecc19856ef49.png)

下記のとおり、計算された結果がコンソールに表示されます。
上記は、変数 a に 42 を代入し、変数 b に 119 を代入し、 a と b を足した結果を c としてコンソールに表示するという処理になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/1bfcb4e0-2355-e776-4f67-e756f4a34c62.png)

次はより複雑な電卓の計算が出来るように、コードを修正します。
チュートリアルにあるコードをそのまま貼り付けては意味がないので、手入力して、１つ１つ意味を確認しながらコードを書いてみます。

```c#

using System;

namespace Calculator
{
    class Program
    {
        static void Main(string[] args)
        {
            // Declare variables and then initialize to zero.
            int num1 = 0; int num2 = 0;

            // Display title as the C# console calculator app.
            Console.WriteLine("Console Calculator in C#\r");
            Console.WriteLine("------------------------\n");

            // Ask the user to type the first number.
            Console.WriteLine("Type a number, and then press Enter");
            num1 = Convert.ToInt32(Console.ReadLine());

            // Ask the user to type the second number.
            Console.WriteLine("Type another number, and then press Enter");
            num2 = Convert.ToInt32(Console.ReadLine());

            // Ask the user to choose an option.
            Console.WriteLine("Choose an option from the following list:");
            Console.WriteLine("\ta - Add");
            Console.WriteLine("\ts - Subtract");
            Console.WriteLine("\tm - Multiply");
            Console.WriteLine("\td - Divided");
            Console.Write("your option?");

            // Yse a switch statement to do the math.
            switch (Console.ReadLine())
            {
                case "a":
                    Console.WriteLine($"Your result: {num1} + {num2} = " + (num1 - num2));
                    break;
                case "s":
                    Console.WriteLine($"Your result: {num1} - {num2} = " + (num1 - num2));
                    break;
                case "m":
                    Console.WriteLine($"Your result: {num1} * {num2} = " + (num1 * num2));
                    break;
                case "d":
                    Console.WriteLine($"Your result: {num1} / {num2} = " + (num1 / num2));
                    break;
            }
            // Wait for the user to respond before closing.
            Console.Write("Press any key to close the Calculator console app...");
            Console.ReadKey();
        }
    }
}

```

上記コードを書いたあとに、F5 キーもしくは「Calculator」ボタンをクリックすると、下記のようにコンソール画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/e41a07cc-3330-03fb-afd7-27e55fc3034e.png)

Num1 に任意の値と入れて「Enter」ボタンを押下、Num2 に任意の値を入れて「Enter」ボタンを押下、どの四則演算をするかを選択し「Enter」を押下すると、計算結果が出力されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/790e65fe-0b5a-3980-2a6d-e38f7bdf2373.png)

ただこの電卓だと、下記のとおり、小数点以下の計算ができないため、小数点を求めるような計算を要求すると「0」になってしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/6684f5f2-9f0d-cf66-3088-e67d2f0a00de.png)

###小数を表示できるようにする

小数を表示できるようにするためには、**int** 変数を **float** 変数に変更する必要があります。
すべての文字を書き換えるのは大変なので、「Ctrl ＋ F」キーを入力して、検索と置換ボックスを開き、変更前の文字を「int」変更後の文字を「float」として、「すべて置換」のボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/fbb392c1-279d-970d-00d8-566993ec4701.png)

すべて置換するかどうかの確認画面が表示されるので、「はい」をクリックし、置換を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/f47ed57d-69ca-37ff-2ef3-566cd171ce37.png)

置換を行ったあとに再度コンソールを起動して同じ計算をすると、下記画面ショットのとおり、小数点を含めた計算結果が出力されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/4f399c61-9492-a00b-44b0-f1612d39afee.png)

###小数の計算をできるようにする

しかし、現在のコードでは下記画面ショットのように、小数点を含んだ数字で計算をしようとするとエラーになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/11b903e4-1d5b-5344-aa7b-a7c06149a5a3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/9c18777f-b2f4-f4c9-56df-f9747cb29606.png)

変数の **float** を**double**に変更し、メソッドを**Convert.ToINT32** から **Convert.ToDouble**に変更します。

Convert.ToInt32 Method について
URL:https://docs.microsoft.com/ja-jp/dotnet/api/system.convert.toint32?view=netframework-4.8

Convert.ToDouble Method について
URL:https://docs.microsoft.com/ja-jp/dotnet/api/system.convert.todouble?view=netframework-4.8

#####メソッドとは
メソッドは一言でいうと、一連の処理をひとまとめにしたものです。
Console.WriteLine というメソッドを今回頻繁に使っていると思いますが、このメソッドは「コンソールに表示するプログラム」をひとまとめにしています。

例えば、ある料理を作るときに、１つ1つの工程を箇条書きで書くことで表現することもできます。
しかし、例えば、「下ごしらえ」「調理」「盛り付け」などように各工程後に区切ってレシピを用意したほうが調理しやすいかと思います。
メソッドというのは、この各工程ごとの処理をひとまとめにしたプログラムを読みやすくするとともに、プログラム自体を簡略化する時に使います。

さて、同じように「Ctrl ＋ F」キーを入力して、検索と置換ボックスを開き変更を行います。
変更を行い、小数点を含んだ計算を行っても、下記のとおり、正しく計算結果が出力されるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/e06d1de2-2d3c-b01b-1db6-9dc4c7c823c0.png)

### 0 で割ったときのエラーを修正する

現在のプログラムで 0 で割ると下記のとおりコンソールが以下のとおりフリーズしてしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/13ab62af-e2d9-dd52-6f39-803101e6f1b0.png)

以下のとおりコードを修正してみます。

変更前)

```c#

                    Console.WriteLine($"Your result: {num1} / {num2} = " + (num1 / num2));
                    break;
```

変更後)

```c#

                   // Ask the user to enter a non-zero divisor until they do so.
                    while (num2 == 0)
                    {
                        Console.WriteLine("Enter a non-zero divisor: ");
                        num2 = Convert.ToInt32(Console.ReadLine());
                    }
                    Console.WriteLine($"Your result: {num1} / {num2} = " + (num1 / num2));
                    break;
```

修正後にコンソール アプリを起動し、「0」で割ろうとすると、「0」以外の値を入力するまで、要求が繰り返されるようになり、コンソールがフリーズしなくなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/1d99bc45-8042-b739-9c18-57d675c7b91b.png)

### 書式エラーを修正する

電卓アプリなので、基本的に数字以外は入力しないのですが、数字以外を入力したときにエラーが出てしまいコンソールがフリーズしてしまうのはよくありません。具体的には下記画面ショットのように、数字以外のフォーマットを入力するとコンソールがフリーズし、エラーが出力されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/8565768e-a7ab-4fe8-a3a6-cb9387d369c0.png)

この問題を解決するためには、対象のコードをリファクタリングする必要があります。
リファクタリングとは、プログラム自体の動きを変更することなく、ソースコードを変更することを意味します。

-参考情報
コードのリファクタリング
URL:https://docs.microsoft.com/ja-jp/visualstudio/ide/refactoring-in-visual-studio?view=vs-2019

###コードを修正する

現在 **class Program** としてクラスを program という名前で処理を行っていますが、そのクラスを**Calculator**と**Program**に分割します。
Calculator クラスでは計算作業を処理させ、 Program クラスではユーザー インターフェースとエラーの作業を処理します。

その前に、そもそも、クラスが理解できていなかったので調べてみました。

#####クラスとは
クラスは、関連のある変数とメソッドをまとめたものです。
この説明は、「確かな力が身につくC#「超」入門 」に記載されている内容が分かりやすかったので引用します。

確かな力が身につくC#「超」入門 
URL:https://www.sbcr.jp/product/4797390261/

絵にすると分かりやすいと思うので絵を描きました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/7ae968c1-b5a9-f875-788d-b91d2525d226.png)

まず、「プレイヤー」と「敵」というクラスを定義します。
次に、プレイヤーと敵で使用する、名前や HP などを「変数」として定義します。
さらに、プレイヤーと敵がどのように動作するかについて、物理攻撃や逃走などの「メソッド」として定義します。

上記のように 1 つのプログラムを書くために必要な**変数**や**メソッド**をまとめたものを**クラス**という単位で管理します。
また、このような考え方を**オブジェクト指向**と呼ぶようです。

では、具体的に、Calculator クラスと Program クラスを修正してみます。

Calculator クラスを以下のように新規に追加します。

```c#

class Calculator
{
    public static double DoOperation(double num1, double num2, string op)
    {
        double result = double.NaN; // Default value is "not-a-number" which we use if an operation, such as division, could result in an error.

        // Use a switch statement to do the math.
        switch (op)
        {
            case "a":
                result = num1 + num2;
                break;
            case "s":
                result = num1 - num2;
                break;
            case "m":
                result = num1 * num2;
                break;
            case "d":
                // Ask the user to enter a non-zero divisor.
                if (num2 != 0)
                {
                    result = num1 / num2;
                }
                break;
            // Return text for an incorrect option entry.
            default:
                break;
        }
        return result;
    }
}
```

続いて、その下に新規に Program クラスを追加します。

```c#

class Program
{
    static void Main(string[] args)
    {
        bool endApp = false;
        // Display title as the C# console calculator app.
        Console.WriteLine("Console Calculator in C#\r");
        Console.WriteLine("------------------------\n");

        while (!endApp)
        {
            // Declare variables and set to empty.
            string numInput1 = "";
            string numInput2 = "";
            double result = 0;

            // Ask the user to type the first number.
            Console.Write("Type a number, and then press Enter: ");
            numInput1 = Console.ReadLine();

            double cleanNum1 = 0;
            while (!double.TryParse(numInput1, out cleanNum1))
            {
                Console.Write("This is not valid input. Please enter an integer value: ");
                numInput1 = Console.ReadLine();
            }

            // Ask the user to type the second number.
            Console.Write("Type another number, and then press Enter: ");
            numInput2 = Console.ReadLine();

            double cleanNum2 = 0;
            while (!double.TryParse(numInput2, out cleanNum2))
            {
                Console.Write("This is not valid input. Please enter an integer value: ");
                numInput2 = Console.ReadLine();
            }

            // Ask the user to choose an operator.
            Console.WriteLine("Choose an operator from the following list:");
            Console.WriteLine("\ta - Add");
            Console.WriteLine("\ts - Subtract");
            Console.WriteLine("\tm - Multiply");
            Console.WriteLine("\td - Divide");
            Console.Write("Your option? ");

            string op = Console.ReadLine();

            try
            {
                result = Calculator.DoOperation(cleanNum1, cleanNum2, op);
                if (double.IsNaN(result))
                {
                    Console.WriteLine("This operation will result in a mathematical error.\n");
                }
                else Console.WriteLine("Your result: {0:0.##}\n", result);
            }
            catch (Exception e)
            {
                Console.WriteLine("Oh no! An exception occurred trying to do the math.\n - Details: " + e.Message);
            }

            Console.WriteLine("------------------------\n");

            // Wait for the user to respond before closing.
            Console.Write("Press 'n' and Enter to close the app, or press any other key and Enter to continue: ");
            if (Console.ReadLine() == "n") endApp = true;

            Console.WriteLine("\n"); // Friendly linespacing.
        }
        return;
    }
}
```

上記のように修正し、電卓アプリにて、「英文字」を入力したり、「0」で割ってもコンソールがフリーズすることなく、計算を継続できることが確認できるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/c3dacb20-c978-d50c-fd88-d7d08b68ece0.png)

最後に、完成したコードは以下のようになります。

```c#

using System;

namespace Calculator
{
    class Calculator
    {
        public static double DoOperation(double num1, double num2, string op)
        {
            double result = double.NaN; // Default value is "not-a-number" which we use if an operation, such as division, could result in an error.

            // Use a switch statement to do the math.
            switch (op)
            {
                case "a":
                    result = num1 + num2;
                    break;
                case "s":
                    result = num1 - num2;
                    break;
                case "m":
                    result = num1 * num2;
                    break;
                case "d":
                    // Ask the user to enter a non-zero divisor.
                    if (num2 != 0)
                    {
                        result = num1 / num2;
                    }
                    break;
                // Return text for an incorrect option entry.
                default:
                    break;
            }
            return result;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            bool endApp = false;
            // Display title as the C# console calculator app.
            Console.WriteLine("Console Calculator in C#\r");
            Console.WriteLine("------------------------\n");

            while (!endApp)
            {
                // Declare variables and set to empty.
                string numInput1 = "";
                string numInput2 = "";
                double result = 0;

                // Ask the user to type the first number.
                Console.Write("Type a number, and then press Enter: ");
                numInput1 = Console.ReadLine();

                double cleanNum1 = 0;
                while (!double.TryParse(numInput1, out cleanNum1))
                {
                    Console.Write("This is not valid input. Please enter an integer value: ");
                    numInput1 = Console.ReadLine();
                }

                // Ask the user to type the second number.
                Console.Write("Type another number, and then press Enter: ");
                numInput2 = Console.ReadLine();

                double cleanNum2 = 0;
                while (!double.TryParse(numInput2, out cleanNum2))
                {
                    Console.Write("This is not valid input. Please enter an integer value: ");
                    numInput2 = Console.ReadLine();
                }

                // Ask the user to choose an operator.
                Console.WriteLine("Choose an operator from the following list:");
                Console.WriteLine("\ta - Add");
                Console.WriteLine("\ts - Subtract");
                Console.WriteLine("\tm - Multiply");
                Console.WriteLine("\td - Divide");
                Console.Write("Your option? ");

                string op = Console.ReadLine();

                try
                {
                    result = Calculator.DoOperation(cleanNum1, cleanNum2, op);
                    if (double.IsNaN(result))
                    {
                        Console.WriteLine("This operation will result in a mathematical error.\n");
                    }
                    else Console.WriteLine("Your result: {0:0.##}\n", result);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Oh no! An exception occurred trying to do the math.\n - Details: " + e.Message);
                }

                Console.WriteLine("------------------------\n");

                // Wait for the user to respond before closing.
                Console.Write("Press 'n' and Enter to close the app, or press any other key and Enter to continue: ");
                if (Console.ReadLine() == "n") endApp = true;

                Console.WriteLine("\n"); // Friendly linespacing.
            }
            return;
        }
    }
}
```

#おわりに
C# を学ぶ第一歩として、Visual Studio を使い、電卓アプリをチュートリアルに従い作成してみました。
このチュートリアルでは、クラス、メソッド、変数などのオブジェクト指向について学べたとともに、Microsoft が用意しているメソッドの使い方を学びました。

プログラミングの考え方、書き方を理解できるようになれば、ぼんやりと見えていたプログラムをかみ砕いて見ることができるようになります。(なると信じています。)

このシリーズを継続していき、C# を身に着けていきたいとおもいます。
少しでもこの記事が参考になれば幸いです。