C#は静的型付けの言語で、すべての変数、フィールド、式はコンパイル時にわかる型を持っています。言語には独自のキーワード(int、double、string など)を持つ組み込み型の集まりがあります。各キーワードは System 名前空間の型の別名なので、int と System.Int32 は同じ型であり、string と System.String も同じ型です。
組み込み型
| 型 | .NETの型 | サイズ | 範囲または精度 | リテラル |
|---|---|---|---|---|
bool | Boolean | 1バイト | true または false | true |
byte | Byte | 1バイト | 0から255 | 200 |
sbyte | SByte | 1バイト | -128から127 | -5 |
short | Int16 | 2バイト | -32,768から32,767 | 1200 |
ushort | UInt16 | 2バイト | 0から65,535 | 60000 |
int | Int32 | 4バイト | -2,147,483,648から2,147,483,647 | 42 |
uint | UInt32 | 4バイト | 0から4,294,967,295 | 42u |
long | Int64 | 8バイト | 約±9.2 × 10^18 | 42L |
ulong | UInt64 | 8バイト | 0から約1.8 × 10^19 | 42ul |
float | Single | 4バイト | 約±3.4 × 10^38、約6から9桁 | 2.5f |
double | Double | 8バイト | 約±1.8 × 10^308、約15から17桁 | 2.5 または 2.5d |
decimal | Decimal | 16バイト | 約±7.9 × 10^28、28から29桁 | 2.5m |
char | Char | 2バイト | UTF-16のコード単位1つ、U+0000からU+FFFF | 'A' |
string | String | 参照 | 任意の長さのテキスト、不変 | "hello" |
object | Object | 参照 | すべての型の基底 | new object() |
接尾辞のない整数リテラルは int で、int に収まらないほど大きければ、uint、long、ulong のうち最初に収まるものになります。小数点のあるリテラルは double です。だから float f = 2.5; と decimal d = 2.5; はコンパイルできません。リテラルは double で、double から float や decimal への暗黙の変換はないからです。接尾辞を付けます:2.5f、2.5m。
すべての数値型は、その限界を定数として公開しています。
出力:
byte: 0 to 255
short: -32768 to 32767
int: -2147483648 to 2147483647
long: -9223372036854775808 to 9223372036854775807
decimal max: 79228162514264337593543950335
double max: 1.798E+308
sizes: int 4, long 8, double 8, char 2
255 161 3000000000
実際には、ほとんどのコードはこのうち4つを使います。数やインデックスには int、IDや大きな合計には long、測定値や数学には double、お金には decimal です。
decimalとdouble
double(と float)は数値を2進数で格納します。0.1を含むほとんどの10進数の小数には正確な2進数の形がないので、表現できる最も近い値として格納され、小さな誤差が積み重なります。decimal は10進数の整数とスケールを格納するので、0.1は正確に0.1です。
出力:
False
True
0.30000000000000004
0.3
double sum == 1.0: False
decimal sum == 1.0: True
Total: 59.97
Split three ways: 19.99
お金、価格、税金など、人が手で足し合わせて比べるものには decimal を使います。入力の末尾のゼロを保ち(1.50m は 1.50 と表示されます)、有効数字は28から29桁です。
物理的な測定値、統計、幾何学、ゲームには double を使います。数倍速く、メモリは半分で、範囲は非常に広いです。計算で得た2つのdoubleを == で比較してはいけません。差を許容誤差と比べます:Math.Abs(x - y) < 1e-9。
decimal は魔法ではありません。1m / 3m はやはり 0.3333333333333333333333333333 です。また、double の演算はゼロ除算で例外を投げず(無限大になり、0.0 / 0.0 ならNaNになります)、decimal と整数のゼロ除算は DivideByZeroException を投げます。
整数のオーバーフローとchecked
型の範囲を超える整数の演算は、既定では何も言わずに一周します。コンパイラは定数式のオーバーフローは検出しますが、値が変数から来る場合は検出しません。
出力:
-2147483648
OverflowException
-1294967296
2147483648
checked(...) か checked { ... } ブロックを使うと、オーバーフローが OverflowException になります。<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow> でプロジェクト全体に対して有効にすることもできます。定数で int x = int.MaxValue + 1; と書くと、コンパイラにはオーバーフローが見えるので、コンパイルエラー(CS0220)になります。
最後の行は、ほとんどのオーバーフローのバグの直し方を示しています。演算の前により広い型に変換します。(long)(max + 1) では、キャストが実行される時点で加算がすでに一周しているので間違いです。
値型と参照型
C#のすべての型は値型か参照型のどちらかで、その違いによって代入の動作が決まります。
- 値型:すべての数値型、
bool、char、DateTime、enum、すべてのstruct。変数はデータを含み、代入するとそれがコピーされます。 - 参照型:
string、配列、object、すべてのclass。変数はオブジェクトへの参照を含み、代入すると参照がコピーされるので、両方の変数が同じオブジェクトを見ます。
出力:
a=10 b=99
first[0]=99
s1=cat s2=cats
string は参照型ですが、実際には値のように振る舞います。不変で、== は参照ではなくテキストを比較します。ユーザー定義の値型について詳しくは構造体を参照してください。
charとbool
char はUTF-16のコード単位1つで、単一引用符で書きます。内部的には数値型なので、int に変換でき、演算もできます。
出力:
66
C
True
True
7
b
True
bool にはちょうど2つの値があり、数値との間で変換されません。if (count) はゼロでないかの判定ではなく、コンパイルエラーです。if (count != 0) と書きます。ほとんどの絵文字のように基本多言語面の外にある文字は2つの char 値(サロゲートペア)を使うので、string の Length が数えるのは見た目の文字ではなくUTF-16の単位です。
既定値
フィールド、配列の要素、default(T) は、その型の既定値になります。数値ならゼロ、bool なら false、char なら '\0'、参照型なら null です。ローカル変数には既定値がなく、使う前に代入しなければなりません。
出力:
0
0
False
True
0001-01-01
0,0,0
C# 7.1以降、コンパイラが推論できる場合は型なしで default リテラルを書けます(int x = default;)。値型は null になれず、そのためにあるのが int?(null許容値型)です。null許容型を参照してください。
ネイティブサイズの整数と大きな整数
特別な場合のために、さらに2つの数値型があります。nint と nuint(C# 9)はポインターと同じサイズ、つまりプラットフォームによって32ビットか64ビットの整数で、相互運用や低レベルのコードで使われます。System.Numerics.BigInteger は、速度と引き換えに任意の大きさの整数を保持します。
using System.Numerics;
BigInteger factorial = 1;
for (int i = 2; i <= 30; i++) factorial *= i;
// 265252859812191058636308480000000
よくある質問
C#のデータ型にはどんなものがありますか?
組み込み型には、整数(sbyte、byte、short、ushort、int、uint、long、ulong)、浮動小数点型の float と double、正確な10進数の計算のための decimal、UTF-16の文字のための char、bool、そして参照型の string と object があります。各キーワードは.NETの型の別名で、int は System.Int32、string は System.String です。
C#でお金を扱うにはdecimalとdoubleのどちらを使うべきですか?
decimal を使います。double は2進数の小数を格納するので、0.1のような値を正確に表せず、合計がずれていきます(0.1 + 0.2 == 0.3 は false)。decimal は10進数の桁を格納し、28から29桁の有効数字を保ち、会計担当者が期待する結果を出します。double は高速で範囲もはるかに広いので、科学計算、グラフィックス、測定値に使います。
C#のintの最大値はいくつですか?
int.MaxValue は2,147,483,647、int.MinValue は-2,147,483,648です(32ビットの符号付き整数)。int.MaxValue に1を足すと、checked のコンテキストで実行しない限り、何も言わずに int.MinValue に一周します。checked の中では OverflowException が投げられます。より大きな値には long(約922京まで)を使います。
C#のfloatとdoubleの違いは何ですか?
float は32ビットで有効数字は約6から9桁、double は64ビットで約15から17桁です。浮動小数点のリテラルは既定で double なので、float には f の接尾辞が必要です:float speed = 2.5f;。メモリやAPI(Unityの Vector3 など)が float を求めるのでない限り、double を使います。
C#の値型と参照型の違いは何ですか?
値型の変数(int、double、bool、DateTime、任意の struct)はデータそのものを保持し、代入するとそれがコピーされます。参照型の変数(string、配列、任意の class)はヒープ上のオブジェクトへの参照を保持し、代入すると参照がコピーされるので、2つの変数が同じオブジェクトを指すことがあります。