CLRの型

CLR (Common Language Runtime)
共通言語ランタイム
CLS (Common Language Specification)
共通言語仕様

事前定義型 (Predefined types)

値型 (Value types)

値型は、データを直接格納します。

null 許容型 (Nullable types)

特定の値型に対して、nullも受け入れるようにできます。

         int  a = null; // error CS0037: Null 非許容の値型であるため、Null を 'int' に変換できません

Nullable<int> b = null;
         int? c = null; // Nullable<T>の省略表現

値がnullであるかどうかは、HasValueの戻り値またはnullと比較することでわかります。

Nullable<int> x = null;
Console.Write(x.HasValue); // False
Console.Write(x == null);  // True

x = 10;
Console.Write(x.HasValue); // True
Console.Write(x.Value);    // 10

x = null;
Console.Write(x.Value); // System.InvalidOperationException「Null 許容のオブジェクトには値を指定しなければなりません。」

型がnull 許容型であるかどうかは、Nullable.GetUnderlyingType()がnullを返さないことで確認できます。c# - How to check if an object is nullable? - Stack Overflow

null許容型を基になる型へ変換するには、明示的なキャストが必要です。ただし値がnullのときにはInvalidOperationException例外が発生し失敗するため、null合体演算子を用いてnullの場合の値を明示します。

Nullable<int> x = null;

int a = x;       // error CS0266: 型 'int?' を 'int' に暗黙的に変換できません。明示的な変換が存在します (cast が不足していないかどうかを確認してください)
int b = (int)x;  // System.InvalidOperationException「Null 許容のオブジェクトには値を指定しなければなりません。」
int c = x ?? -1; // -1

タプル型 (Tuple types)

実体はValueTuple構造体です。

タプル型 - C# リファレンス - C# | Microsoft Learn

参照型 (Reference types)

参照型は、データへの参照を格納します。

単純型 (Simple types)

単純型とは、コンパイラが直接サポートするデータ型です。この型は既存のFCLに直接マッピングされるため、次の4つのコードはすべて同一の結果をもたらします。

int          a = 0;
int          a = new int();

System.Int32 a = 0;
System.Int32 a = new System.Int32();
C#の型 FCLの型 CLS準拠 基本型※1 内容
bool System.Boolean trueまたはfalse
char System.Char 16ビット Unicode文字 (UTF-16)
sbyte System.SByte × 8ビット 符号付き整数値
byte System.Byte 8ビット 符号なし整数値
short System.Int16 16ビット 符号付き整数値
ushort System.UInt16 × 16ビット 符号なし整数値
int System.Int32 32ビット 符号付き整数値
uint System.UInt32 × 32ビット 符号なし整数値
long System.Int64 64ビット 符号付き整数値
ulong System.UInt64 × 64ビット 符号なし整数値
float System.Single 32ビット 浮動小数点型
double System.Double 64ビット 浮動小数点型
decimal System.Decimal × 128ビット 浮動小数点型
組み込み型の一覧表 (C# リファレンス) | Microsoft Learn ※1 基本型 / プリミティブ型 (primitive types) Remarks - Type.IsPrimitiveImpl Method (System) | Microsoft Learn

参考

一意ではない型

暗黙的に型指定されるローカル変数

型を明示せず、その決定をコンパイラに委ねます。

var i = 10;

varはローカルの変数のみで有効であり、関数の戻り値などには使用できません。

var Func() // error CS0825: コンテキスト キーワード 'var' は、ローカル変数宣言内またはスクリプト コード内でのみ有効です。
{
    return 1;
}

関数をローカルとしても同じです。

Func<var> func = () => // error CS0825
{
    return 1;
};

匿名型 (Anonymous types)

型の定義なく、読み取り専用のプロパティを単一のオブジェクトにカプセル化できます。

var x = new { Num = 1, Text = "A" };

int a = x.Num; // ok
x.Num = 2; // error CS0200: プロパティまたはインデクサー '<anonymous type: int Num, string Text>.Num' は読み取り専用であるため、割り当てることはできません

dynamic型

コンパイラによる型チェックを回避できるobject型です。コンパイル時にはobject型に変換されるため、コンパイル時にのみ意味を持ちます。

型変換 (Type Conversions)

型変換の失敗は、コンパイル エラーとして検出されます。

int i = 10;
double d = i;

int i1 = d;      // error CS0266: 型 'double' を 'int' に暗黙的に変換できません。明示的な変換が存在します (cast が不足していないかどうかを確認してください)
int i2 = (int)d; // ok
string str = "sample";
int i1 = str;      // error CS0029: 型 'string' を 'int' に暗黙的に変換できません
int i2 = (int)str; // error CS0030: 型 'string' を 'int' に変換できません

派生型への変換など、実行時に型変換に失敗するとInvalidCastExceptionが投げられます。

object o = new object();
int i1 = (int)o; // System.InvalidCastException:指定されたキャストは有効ではありません。(Specified cast is not valid.)

int i = 10;
object o1 = i; // ok

型変換が可能であれば良いため、nullが格納された参照型も変換できます。

安全な型変換

as演算子

変換可能ならば変換し、不可能ならば例外を投げずにnullを返します。as 演算子 - 型のテスト演算子とキャスト式 - C# リファレンス | Microsoft Learn

expression as type

とすることは、次に同じです。

expression is type ? (type)expression : (type)null

変換不可能な場合を想定しないのならば、as演算子を用いず例外を投げさせます。

object o1 = 1;
object o2 = "sample";

string r1 = o1 as string; // null
string r2 = o2 as string; // "sample"

is演算子

指定の型に変換可能か調べられます。可能ならばtrue、さもなくばfalseが返されます。is 演算子 - C# リファレンス | Microsoft Learn

expression is type

変換可能であるかで判定されるため、typeから派生したり、インターフェイスであるtypeを実装する場合もtrueとなります。よって特定の型と等しいかどうかは、Typeクラスの等値演算子で評価します。

int[] a = new int[0];
bool r1 = a is IList; // true
bool r2 = a.GetType() == typeof(IList); // false

この演算子は変換の可否を調べるだけの場合に用います。変換を伴う操作ならばas演算子で代用でき、is演算子で確認後に変換するような処理に対しては、CA1800として警告されます。ただしnull 非許容の型にはas演算子を適用できないため、この演算子を用います。

object o = 1;
int r1 = o as int; // error CS0077: as 演算子は参照型または Null 許容型で使用してください ('int' は Null 非許容の値型です)

if (o is int)
{
    int r2 = (int)o;
}

型変換のクラス

  • System.Convertクラス … 基本データ型 (base data type) から、他の基本データ型への変換
  • System.ComponentModel.TypeConverterクラス … 他の型への変換や、標準値やサブプロパティにアクセスするための統一された方法を提供

ボックス化 (ボクシング / boxing)

ボックス化とは、値型からobject型 (またはその値型によって実装されているインターフェイス型) への変換処理のことです。

ボックス化とボックス化解除 - C# プログラミング ガイド | Microsoft Learn

ボックス化解除 (アンボクシング / unboxing)

型情報の取得

型の宣言の情報は、System.Typeクラスから取得できます。

Typeクラスのオブジェクトの取得

Typeクラスのオブジェクトは、次の方法により取得できます。

typeof演算子 (typeof expression)

型を指定して取得できます。typeof (C# リファレンス) | MSDN

Type type = typeof(int); // FullName = "System.Int32"

このとき型の名前を得るのが目的ならば、nameof式を用います。

GetTypeメソッド

インスタンスから取得できます。Object.GetType Method (System) | Microsoft Learn

int a = 1;
Type type = a.GetType(); // FullName = "System.Int32"

基本クラスに格納されていても、そのインスタンスの実際の型が返されます。

object o = 1;
Type type = o.GetType(); // FullName = "System.Int32"

または文字列から取得できます。Type.GetType Method (System) | Microsoft Learn

Type type = Type.GetType("System.Int32");

このとき文字列は、typeof(TypeNames).AssemblyQualifiedNameで得られるアセンブリ修飾名 (assembly-qualified name) で指定します。

Type.GetType("int");          // null
Type.GetType("Int32");        // null
Type.GetType("system.int32"); // null
Type.GetType("System.Int32"); // OK

文字列が有効な名前であっても、その型がアクセスできない保護レベルとなっていればnullが返されます。

型のサイズの取得

sizeof演算子

マネージド メモリに割り当てられたバイト数を取得できます。sizeof (C# リファレンス) | MSDN

int a1 = sizeof(int);   // 4
int a2 = sizeof(Int32); // 4

Marshal.SizeOf()メソッド

アンマネージド オブジェクトのバイト数を取得できます。

public static int SizeOf(
    object structure
)
Marshal.SizeOf メソッド (Object) (System.Runtime.InteropServices) | MSDN

インスタンスのバイト単位のアンマネージド サイズを取得できます。

int x = 0;
int a = Marshal.SizeOf(x); // 4

オブジェクトから取得できるオーバーロードも用意されています。

int a1 = Marshal.SizeOf(typeof(int));
int a2 = Marshal.SizeOf<int>(); // .NET Framework 4.5.1 以降でサポート
Microsoft Learnから検索