プログラム初心者のためのC#入門 #8 構造体

# #8 構造体
構造体は違う型のデータをひとまとめにして扱うことができるようにしたものです．構造体の話に入る前にまずは次の問題を解いてみてください．

---
**問題**

|安藤|小林|戸田|中野|渡辺|
|:-:|:-:|:-:|:-:|:-:|
|83|59|72|96|81|
上の表は英語のテストを受けた5人の得点です．この5人の名前を点数が昇順(低い順)になるように画面に出力してください．

---
とりあえず点数を入れるint型の配列を用意して，ソートは

```cs
static int[] BubbleSort(int[] scores)
{
    var ary = new int[scores.Length];

    for (int i = 0; i < scores.Length; i++)
    {
        ary[i] = scores[i];
    }

    for (int i = 1; i < ary.Length; i++)
    {
        for (int j = ary.Length - 1; j >= i; j--)
        {
            if (ary[j - 1] > ary[j])
            {
                int temp = ary[j - 1];
                ary[j - 1] = ary[j];
                ary[j] = temp;
            }
        }
    }

    return ary;
}
```

こんな感じの関数を作って配列を入れればあっという間に...

```:出力結果
59
72
81
83
96
```

はい！違いますね！
名前を出さないといけないので，点数だけ並べ替えても意味がありません．名前と点数をまとめて扱って一緒に並べ替える必要がありそうです．そこで構造体を使います．

```
struct 構造体名
{
    // 変数
}
```

こういう形式で書くことで，型として使える構造体を定義できます．ちなみに，構造体の中に定義された変数(`static`修飾子が付いたものを除く)をメンバ変数やフィールドと呼びます．変数に`public`修飾子が付いたものは構造体の外部からアクセスできます．

```StructureSample.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StructureSample
{
    struct ScoreData
    {
        public string name;
        public int score;
    }

    class Program
    {
        static void Main(string[] args)
        {
            ScoreData ando = new ScoreData();
            ando.name = "安藤";
            ando.score = 83;
            Console.WriteLine(ando.name);
            Console.WriteLine(ando.score);
        }
    }
}
```

`name`や`score`という変数がScoreDataの中に元々あるわけではなく，`ando`のように実際の変数(**インスタンス**という)を作ると，その変数の中に`name`，`score`が入っている状態になります．
とりあえずScoreData型の変数`ando`に名前と得点のデータを持たせることができました．これをScoreData型の配列に入れて，配列をソートする関数を作って渡してやればよさそうです．

```cs
ScoreData ando = new ScoreData();
ando.name = "安藤";
ando.score = 83;

ScoreData kobayashi = new ScoreData();
kobayashi.name = "小林";
kobayashi.score = 59;

ScoreData toda = new ScoreData();
toda.name = "戸田";
toda.score = 72;

ScoreData nakano = new ScoreData();
nakano.name = "中野";
nakano.score = 96;

ScoreData watanabe = new ScoreData();
watanabe.name = "渡辺";
watanabe.score = 81;

ScoreData[] scoreDatas = new ScoreData[5]
{
    ando,
    kobayashi,
    toda,
    nakano,
    watanabe
};
```

面倒ですね．後で配列に入れてしまってインデックスで管理するのにわざわざ変数を作ってそれぞれメンバ変数に代入するのがちょっと手間に感じてしまいます．もっと簡潔に書けるようにしましょう．

```StructureSample.cs
struct ScoreData
{
    public string name;
    public int score;

    // ↓追加する．
    public ScoreData(string name, int score)
    {
        this.name = name;
        this.score = score;
    }
}
```

追加した関数のようなものはコンストラクタと呼ばれ，`new ScoreData()`されたときに一度だけ実行されるものです．コンストラクタは戻り値を持たず，引数は`new`したときに渡すことができます．コンストラクタに引数を渡してメンバ変数を初期化してみましょう．

```StructureSample.cs
static void Main(string[] args)
{
    ScoreData ando = new ScoreData("安藤", 83);
    Console.WriteLine(ando.name);
    Console.WriteLine(ando.score);
}
```
```:出力結果
安藤
83
```

これで値を初期化できることはわかりました．言ってみれば`new ScoreData("安藤", 83)`はScoreData型のリテラルのようなものなので，変数に代入せずに配列に入れることができます．

```StructureSample.cs
static void Main(string[] args)
{
    ScoreData[] scoreDatas = new ScoreData[5]
    {
        new ScoreData("安藤", 83),
        new ScoreData("小林", 59),
        new ScoreData("戸田", 72),
        new ScoreData("中野", 96),
        new ScoreData("渡辺", 81)
    };
}
```

ではソート関数を作って点数が昇順になるように並べ替えましょう．

```StructureSample.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StructureSample
{
    struct ScoreData
    {
        public string name;
        public int score;

        public ScoreData(string name, int score)
        {
            this.name = name;
            this.score = score;
        }
    }

    class Program
    {
        // ソート関数
        static ScoreData[] BubbleSort(ScoreData[] scoreDatas)
        {
            var ary = new ScoreData[scoreDatas.Length];

            for (int i = 0; i < scoreDatas.Length; i++)
            {
                ary[i] = scoreDatas[i];
            }

            for (int i = 1; i < ary.Length; i++)
            {
                for (int j = ary.Length - 1; j >= i; j--)
                {
                    if (ary[j - 1].score > ary[j].score)
                    {
                        ScoreData temp = ary[j - 1];
                        ary[j - 1] = ary[j];
                        ary[j] = temp;
                    }
                }
            }

            return ary;
        }

        static void Main(string[] args)
        {
            ScoreData[] scoreDatas = new ScoreData[5]
            {
                new ScoreData("安藤", 83),
                new ScoreData("小林", 59),
                new ScoreData("戸田", 72),
                new ScoreData("中野", 96),
                new ScoreData("渡辺", 81)
            };

            foreach (ScoreData scoreData in BubbleSort(scoreDatas))
            {
                Console.WriteLine(scoreData.name);
            }
        }
    }
}
```

```:出力結果
小林
戸田
渡辺
安藤
中野
```

できました．構造体は違う型のデータを複数扱うことができるので使えるようになるととても便利です．自分で作って使うことができるようにしておきましょう．

##LINQ編
以前にも紹介したLINQを使って解く場合を載せておきます．詳しい説明はしないので興味のある人は調べてみてください．

```StructureSample.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StructureSample
{
    struct ScoreData
    {
        public string name;
        public int score;

        public ScoreData(string name, int score)
        {
            this.name = name;
            this.score = score;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var scoreDatas = new[]
            {
                new ScoreData("安藤", 83),
                new ScoreData("小林", 59),
                new ScoreData("戸田", 72),
                new ScoreData("中野", 96),
                new ScoreData("渡辺", 81)
            };

            foreach (var scoreData in scoreDatas.OrderBy(e => e.score))
            {
                Console.WriteLine(scoreData.name);
            }
        }
    }
}
```

`コレクション.OrderBy()`で昇順ソートしています．`e => e.score`は**ラムダ式**という関数のようなものです．
次回は[オブジェクト指向](https://qiita.com/17t7037t/items/b2d48cda496f72555824)について説明します．

##練習問題
||安藤|小林|戸田|中野|渡辺|
|:-:|:-:|:-:|:-:|:-:|:-:|
|英語|83|59|72|96|81|
|数学|75|90|79|61|86|
上の表は英語と数学のテストを受けた5人の得点です．この5人の中で，英語の点数，数学の点数，合計の点数が最も高い人をそれぞれ画面に出力してください．

<details><summary>解答例</summary><div>
`コレクション.OrderByDescending()`で降順ソートしてます．

```StructureSample.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StructureSample
{
    struct ScoreData
    {
        public string name;
        public int englishScore;
        public int mathScore;

        public ScoreData(string name, int englishScore, int mathScore)
        {
            this.name = name;
            this.englishScore = englishScore;
            this.mathScore = mathScore;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            ScoreData[] scoreDatas = new ScoreData[5]
            {
                new ScoreData("安藤", 83, 75),
                new ScoreData("小林", 59, 90),
                new ScoreData("戸田", 72, 79),
                new ScoreData("中野", 96, 61),
                new ScoreData("渡辺", 81, 86)
            };

            Console.WriteLine("英語 : " + scoreDatas.OrderByDescending(e=>e.englishScore).ToArray()[0].name);
            Console.WriteLine("数学 : " + scoreDatas.OrderByDescending(e => e.mathScore).ToArray()[0].name);
            Console.WriteLine("合計 : " + scoreDatas.OrderByDescending(e => e.englishScore + e.mathScore).ToArray()[0].name);
        }
    }
}
```

</div></details>