値型は、データを直接格納します。
特定の値型に対して、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
実体はValueTuple構造体です。
タプル型 - C# リファレンス - C# | Microsoft Learn
参照型は、データへの参照を格納します。
単純型とは、コンパイラが直接サポートするデータ型です。この型は既存の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ビット 浮動小数点型 |
型を明示せず、その決定をコンパイラに委ねます。
var i = 10;
varはローカルの変数のみで有効であり、関数の戻り値などには使用できません。
var Func() // error CS0825: コンテキスト キーワード 'var' は、ローカル変数宣言内またはスクリプト コード内でのみ有効です。
{
return 1;
}
関数をローカルとしても同じです。
Func<var> func = () => // error CS0825
{
return 1;
};
型の定義なく、読み取り専用のプロパティを単一のオブジェクトにカプセル化できます。
var x = new { Num = 1, Text = "A" }; int a = x.Num; // ok x.Num = 2; // error CS0200: プロパティまたはインデクサー '<anonymous type: int Num, string Text>.Num' は読み取り専用であるため、割り当てることはできません
コンパイラによる型チェックを回避できるobject型です。コンパイル時にはobject型に変換されるため、コンパイル時にのみ意味を持ちます。
型変換の失敗は、コンパイル エラーとして検出されます。
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が格納された参照型も変換できます。
変換可能ならば変換し、不可能ならば例外を投げずに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"
指定の型に変換可能か調べられます。可能ならば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;
}
ボックス化とは、値型からobject型 (またはその値型によって実装されているインターフェイス型) への変換処理のことです。
ボックス化とボックス化解除 - C# プログラミング ガイド | Microsoft Learn型の宣言の情報は、System.Typeクラスから取得できます。
Typeクラスのオブジェクトは、次の方法により取得できます。
型を指定して取得できます。typeof (C# リファレンス) | MSDN
Type type = typeof(int); // FullName = "System.Int32"
このとき型の名前を得るのが目的ならば、nameof式を用います。
インスタンスから取得できます。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 (C# リファレンス) | MSDN
int a1 = sizeof(int); // 4 int a2 = sizeof(Int32); // 4
アンマネージド オブジェクトのバイト数を取得できます。
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 以降でサポート