C#のデータ型を説明してみた

　この文章はC#入門者が半ば自分のメモ書きのように書いたものです。おそらく間違いは多々あるとは思いますが、温かく見守ってください。本の内容を自分なりに解釈して、また実験を行っての「当時の理解」が記事に反映されています。
　また、プログラム内部の変数名などに全大文字のローマ字書きが使われています、お気をつけください。逆に全大文字の部分は自由に変更しても大丈夫な部分です。

------

# C#で扱うデータに関して<sub>の基礎<sub><sub>のメモ書き</sub></sub></sub>

## 数字データに関して

・C#では、変数の型と変数の名前を宣言して定義する。

　例えば下記のような形。

```c#:sample
int HENSU = 10;
const int TEISU = 10;
```

　ここで使用した`const`は宣言した変数を定数とする修飾子で、宣言時に代入した数値を保持し続けるように変えるものである。これ以後、`const`で宣言された数値は定数となり、**定数に再代入を行うプログラムはエラーを返す**ようになる。

　また、**定数を作る際には初期値を与える必要がある**。変数のように枠だけ作っておく、という事は出来ない。

　変数には様々な型があり、それぞれ使える範囲が決まっている。例えば`byte`であれば0~2 <sup>8</sup>-1までの正の整数、`int`であれば±(2<sup>31</sup>-1)までの整数、小数点以下を扱う際には`float`や`double`、`decimal`という型もある。

　ここで、最大値に-1が付いている理由は数字の初めが0だからである。8ビットで表すことのできるパターンは256通りで、そのうち一つを0に割り当てる必要があるためにそうなっている。

　`sbyte`と書けば`byte`と同じデータサイズで符号をつけることができたり（つまり範囲は-2<sup>7</sup>~2<sup>7</sup>-1）、`uint`と書けば`int`と同じデータサイズで正の数のみのデータ（つまり範囲は0~2<sup>32</sup>-1）になったりする。
　これは整数を扱う型に限られ、sを付けるかuを付けるかは元の型に符号が付いているか否かで判別する。

　以下に数字を扱う型の一覧を示す。

------

__整数を扱う型__

| 型の名前 | サイズ(bit) |                                                 範囲 | 規定値 |
| :------- | ----------: | ---------------------------------------------------: | -----: |
| byte     |           8 |                                              0 ~ 255 |      0 |
| short    |          16 |                                         -32768~32767 |      0 |
| int      |          32 |                         -2,147,483,648~2,147,483,647 |      0 |
| long     |          64 | -9,223,372,036,854,775,808~9,223,372,036,854,775,807 |     0L |

------

__小数点以下も扱える型__

| 型の名前 | サイズ(bit) | 規定値 |
| :------- | ----------: | -----: |
| float    |          32 |   0.0F |
| double   |          64 |   0.0D |
| decimal  |         128 |   0.0M |

＊範囲は小数点の位置によって変動する。
＊**`double`型よりも`decimal`型のほうが数値精度が高い**が、**数値範囲は`float`型よりも`decimal`型のほうが小さい。**また、CPUによっては**`decimal`型そのものがサポートされていない**ことがあり、**計算速度が圧倒的に遅くなることがある**ようだ。

------

### 数字データの格納/代入に関して

　作成した`byte`データを`byte`型よりデータサイズの大きい`int`型に格納することは可能であるが、その逆は不可能である。ただし、とある宣言を行うことで強引に変換することもできる。

```c#:キャスト
//代入元データ
byte MOTO_BYTE = 100;
int MOTO_INT = 200;

byte SAKI_A = MOTO_BYTE;//可能
byte SAKI_B = MOTO_INT;//不可能
byte SAKI_C = (byte)MOTO_INT;//可能
```

　`byte e = (byte)b;`の行で、`(byte)`と代入する側に付記している。これにより`int`型の数値を`byte`型に変換しているため、この式の内部では`byte`型に`byte`型を代入していることになっていて、エラーとならない。ただし、この手法には問題もあり、例えば下のようなプログラムは正しい値を返すことはない。

```c#:キャストエラー
int MOTO = 500;
byte SAKI_A = (byte)a;//bの値は244
byte SAKI_B = (byte)(a*-1);//cの値は12
byte CHOKUSETU = 500;//これはエラーとなる
```

　これはビットで計算した際に`int`型で格納されているデータの9ビット以上の値が`byte`型に変換される際に切り捨てられる（厳密に演算したとしても下8ビットに影響を及ぼさない）ことに原因がある。これによって256を表す9ビット目が切り捨てられ、244という値になっている。

　cの値が244とならないのは二進数における負の数の表し方に原因があり、00000...000と32個0が連なった`int`における規定値0のデータを負の方向にオーバーフローさせた11111...111を-1としていて、-2,-3と値が下がっていくごとに2進数を繰り下げていく方式であるために、負の数の下8ビットだけを参照した際に、8ビットすべてが1の値、つまり256から244を引いた値で12となっている。

　500を直接`byte`型に代入しようとするとエラーとなる。オーバーフロー/アンダーフローが起きるのはあくまでも変数から変数への代入を強引に（型変換を用いて）行った場合だけである。
　ちなみに500を`sbyte`型に代入した場合は-12となり、-500を代入した場合は12となる。なんとなく不思議な気分だ。

## その他のデータに関して

　C#で数字以外のデータを扱う型で簡単に触れられると私が思うのは`bool`型、`char`型、`string`型の三種である。
　以下でそれぞれの型についてまとめる。

------

__＊__`bool`__型__（論理型）

　`true`または`false`の二値を格納する型。比較演算子を用いた演算結果の格納にも用いることが出来る。よくフラグ管理に使う。
　外部からの代入は`true`または`false`を受け付ける。条件式が真か偽かという結果もここで保持することができる。
　データサイズは8bit。

------

__＊__`char`__型__（文字型）

　文字を一つだけ格納する型。手動で格納する際には\`（シングルクォーテーション）で文字を囲んで格納する。個人的に使用したのはデータ転送で一文字づつ送られてくるものを保存するときのみ。もしかすると後述の`string`型で代用できるかもしれない。`\u〇〇〇〇`という形で直接文字コードで生成することも出来る。（`〇`には16進数が入る）
　データサイズは16bit。

------

__＊__`string`__型__（文字列型）

　文字列を格納する型。手動で格納する際には\"（ダブルクォーテーション）で文字を囲んで格納する。シングルクォーテーション二回ではない。~~最初は騙された。~~数値を扱う方を含む、以上に示した型のどれとも違い、変数の中に直接格納するものではなくどこかにあるデータのアドレスを格納する型である。その二種に関してはいずれ触れるかもしれない。
　データサイズは前後し、最大で2GBとのこと。

> The maximum size of a String object in memory is 2GB, or about 1 billion characters.
> `String`オブジェクトの最大メモリは2GB、またはおよそ十億文字。
>
> https://docs.microsoft.com/ja-jp/dotnet/api/system.string ：stringクラス解説の注釈欄より抜粋

------



## ユーザーが中身を定義する型に関して

　`c#`には最初から用意されている前述の10（+α）種類のデータ形式だけでなく、ユーザーが形式を定義出来る型が用意されている。
　その型とは**列挙型**、**配列**、**構造体**の三つである。

### 列挙型に関して

　`enum 名前:型名(整数型に限る){ 中身1,中身2, ... ,中身N };`のような形で宣言できる型。`:型名`の部分は省略でき、省略した場合は自動的に中身は`int`型になる。中身に対して何も代入しないと、中身1,中身2,...には0から順に番号が代入されていく。N番目の中身にはN-1番が代入される。途中の中身にも番号を代入することが出来る。代入した中身の後ろには、そのまま順番に番号が代入される。
　また、中身に格納した数字を取り出すには型宣言が必要になる。

```c#:enum
enum REKKYO:byte{
    NAKAMI1,NAKAMI2 = 10,
    NAKAMI3
}
System.Console.WriteLine(REKKYO.NAKAMI3);//出力:NAKAMI3
System.Console.WriteLine((byte)NAMAE.NAKAMI3);//出力:11
```

### 配列型に関して

```c#:array
//bool以外の型であればよい
int[] HAIRETU;
HAIRETU = new int[3];
/***/
int[] HAIRETU2 = new int[3];
/***/
int[] HAIRETU3 = new int[3] {0,1,2}
```

　上記/***/で区切られた文章それぞれが別の配列型を宣言する方法である。上から順に私の思う使い道を述べていく。

1. とりあえず配列の名前だけ作っておいて、分岐先で大きさを分けることが出来る。条件によって要素数が変わるときに使える。
2. とりあえず空配列ができる。後から要素を埋めるが、その要素が決まっていない際に使える。
3. 要素の決まった配列が作れる。暫定的、もしくは変動しない要素を作るときに使える。（ReadOnlyと併用するとよい？）

### 構造体に関して

```c#:stract
public struct KOZOTAI{
    public int SUUJI;
    public string MOJI;
}
```

　こいつに関してはよくわかってない。多分classを作るときのようなことをすると思われる。万が一これを目当てにしてきた人にはごめんなさい。ここまでの間違いまくりのどや顔解説も出来ないくらいわかってない。なんだこれは。

　まず、`Main()`の外で上記コードみたいな感じで関数を作る。そのあと、`Main()`の中で`KOZOTAI`型の変数？を宣言することで空の`KOZOTAI`型を作ることが出来る。ここでは`KOZOTAI`型変数？の名称を`SHISAKU`とする。

```c#:struct2
KOZOTAI SHISAKU; //これで空っぽのKOZOTAI型ができる。初期化は出来ない。初期化にはまた別の関数を作る必要があるらしい。
string TEST;	//こちらは比較用。なんとなく似た形になっているのがわかる。
```

　`KOZOTAI`型の変数`SHISAKU`は`SHISAKU.SUUJI`と`SHISAKU.MOJI`からなっていて、それぞれ多分既定値が入っている。ここから、`SHISAKU.SUUJI`と`SHISAKU.MOJI`を編集することが出来る。

```c#:struct3
SHISAKU.SUUJI = 100;
SHISAKU.MOJI = "文字";//ここではstring型やint型の編集ルールをそのまま用いる。
```

　以上で、`SHISAKU.SUUJI`と`SHISAKU.MOJI`の二つの要素を持つ構造体の`SHISAKU`が完成した。なんだこれは。
　

*追記
　大体`class`と似たようなことができるらしい。`class`よりだいぶ軽いけどその分制約（初期化はダメ、継承不可等）が多いらしい。あと`class`は参照型だけど構造体、`struct`は値型だとか。以下のサイトで長野 透様がわかりやすく解説してくださっていました。

> <https://www.sejuku.net/blog/59663> 『【C#】構造体の使い方(クラスとの違い、初期化や配列の使用も解説)』