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

参照型 (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準拠 内容
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

参考

一意ではない型

匿名型 (Anonymous types)

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

var i = 10;

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' に変換できません

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

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

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

安全な型変換

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クラス … 基本データ型から、他の基本データ型への変換
  • System.ComponentModel.TypeConverterクラス … 他の型への変換や、標準値やサブプロパティにアクセスするための統一された方法を提供

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

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

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

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

型の情報の取得

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

Typeクラス

型の宣言の情報を表すクラスです。Type クラス (System) | Microsoft Learn

プロパティ 内容
string FullName 型の完全修飾名 (fully qualified name)
bool IsArray 型が配列であるならば、true。int[]はtrueだが、ArrayやIEnumerable<T>はfalseとなる Remarks - Type.IsArray Property (System) | Microsoft Learn
bool IsClass 型がクラスまたはデリゲートならば、true
     
Type type = typeof(int);
string s1 = type.Namespace; // "System"
string s2 = type.Name;      // "Int32"
string s3 = type.FullName;  // "System.Int32"
string s4 = type.AssemblyQualifiedName; // "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
メソッド 機能
MakeArrayType() 1次元配列として表されるTypeオブジェクトを得られる
MakeByRefType() ref修飾子を付けて渡されるときのTypeオブジェクトを得られる

つまりType.GetType("System.Object&")として得られるTypeを、typeof(System.Object).MakeByRefType()で得られる

   

メンバの情報の取得

たとえば次のような型があるとすると、

public class MyClass
{
    private int field1;
    public int field2;

    private void Method1(int a, int b) { }
    public void Method2(int a, int b) { }

    public int Property1 { get; }
}

この型のTypeから、メンバの情報を取得できます。

public MemberInfo[] GetMembers()
Type.GetMembers メソッド (System) | Microsoft Learn

返されるメンバは、アルファベット順や宣言順などの特定の順番とはなりません。Remarks - Type.GetMembers Method (System)

Type type = typeof(MyClass);

MemberInfo[] memberInfos = type.GetMembers(); // すべてのパブリック メンバ

ConstructorInfo[] ConstructorInfos = type.GetConstructors(); // すべてのパブリック コンストラクタ
FieldInfo[] fieldInfos = type.GetFields();                   // すべてのパブリック フィールド
MethodInfo[] methodInfos = type.GetMethods();                // すべてのパブリック メソッド
PropertyInfo[] propertyInfos = type.GetProperties();         // すべてのパブリック プロパティ

Console.Write(fieldInfos[0].Name); // field2
GetConstructor()

バインディング制約を指定して、コンストラクタを検索できます。

[System.Runtime.InteropServices.ComVisible(true)]
public System.Reflection.ConstructorInfo GetConstructor (
    System.Reflection.BindingFlags bindingAttr, // バインディング制約
    System.Reflection.Binder binder,
    Type[] types,
    System.Reflection.ParameterModifier[] modifiers
    );
GetConstructor(BindingFlags, Binder, Type[], ParameterModifier[]) - Type.GetConstructor メソッド (System) | Microsoft Learn
Type type = typeof(MyClass);

// 引数のないコンストラクタの呼び出し
ConstructorInfo info1 = type.GetConstructor(
    BindingFlags.Instance | BindingFlags.Public,
    null,
    Type.EmptyTypes,
    null);
MyClass myClass1 = (MyClass)info1.Invoke(null);

// 引数が(int, double)であるコンストラクタの呼び出し
ConstructorInfo info2 = type.GetConstrctor(
    BindingFlags.Instance | BindingFlags.Public,
    null,
    new[] { typeof(int), typeof(double) },
    null);
MyClass myClass2 = (MyClass)info2.Invoke(new object[] { 1, 2.0 });

Activator.CreateInstance()ならば、直接インスタンスを作成できます。

public static object CreateInstance (
    Type type,
    System.Reflection.BindingFlags bindingAttr,
    System.Reflection.Binder binder,
    object[] args,
    System.Globalization.CultureInfo culture
    );
CreateInstance(Type, BindingFlags, Binder, Object[], CultureInfo) - Activator.CreateInstance メソッド (System) | Microsoft Learn
// 引数のないコンストラクタの呼び出し
MyClass obj1 = (MyClass)Activator.CreateInstance(
    type,
    BindingFlags.Instance | BindingFlags.Public,
    null,
    null,
    CultureInfo.InvariantCulture);

// 引数のあるコンストラクタの呼び出し
MyClass obj2 = (MyClass)Activator.CreateInstance(
    type,
    BindingFlags.Instance | BindingFlags.Public,
    null,
    new object[] { 1, 2.0 },
    CultureInfo.InvariantCulture);
GetField()

バインディング制約を指定して、フィールドを検索できます。

public abstract System.Reflection.FieldInfo GetField (
    string name,                               // フィールドの名前
    System.Reflection.BindingFlags bindingAttr // バインディング制約
    );
GetField(String, BindingFlags) - Type.GetField メソッド (System) | Microsoft Learn

取得時にBindingFlags.NonPublicを指定すると非パブリックなメンバの情報も取得でき、これを介すとprivateなメンバへもアクセスできます。そして設定はFieldInfo.SetValue()、取得はFieldInfo.GetValue()で行えます。

このとき対象がインスタンスのフィールドならばそのインスタンスを、クラスのそれならばnullを渡します。

MyClass obj = new MyClass();
// obj.field1 = 1; // error CS0122: 'MyClass.field1' はアクセスできない保護レベルになっています

Type type = typeof(MyClass);
FieldInfo field1 = type.GetField("field1", BindingFlags.Instance | BindingFlags.NonPublic);

field1.SetValue(obj, 1); // ok
int value = (int)field1.GetValue(obj);
GetMethod()

バインディング制約を指定して、メソッドを検索できます。

public System.Reflection.MethodInfo GetMethod (
    string name,
    System.Reflection.BindingFlags bindingAttr
    );
GetMethod(String, BindingFlags) - Type.GetMethod メソッド (System) | Microsoft Learn
Type type = typeof(MyClass);
MethodInfo method1 = type.GetMethod("Method1", BindingFlags.Instance | BindingFlags.NonPublic);

MyClass obj = new MyClass();
method1.Invoke(obj, new object[] { 1, 2 });

メソッドにオーバーロードがあると「あいまいな一致が見つかりました。」としてAmbiguousMatchExceptionが投げられるため、引数の型も指定します。

public System.Reflection.MethodInfo GetMethod (
    string name,
    System.Reflection.BindingFlags bindingAttr,
    System.Reflection.Binder binder,
    Type[] types,
    System.Reflection.ParameterModifier[] modifiers
    );
MethodInfo method1 = type.GetMethod(
    "Method1",
    BindingFlags.Instance | BindingFlags.NonPublic,
    null,
    new[] { typeof(int), typeof(int) },
    null);

静的なメソッドを呼び出すならば、GetMethod()のbindingAttrにBindingFlags.Staticを指定します。

public object Invoke (
    object obj,         // メソッドまたはコンストラクタを呼び出すクラスのオブジェクト
    object[] parameters // メソッドまたはコンストラクタへ渡す引数のリスト
    );
Invoke(Object, Object[]) - MethodBase.Invoke メソッド (System.Reflection) | Microsoft Learn

呼び出すのが静的なメソッドならば、objは無視されます。引数がないならば、parametersはnullとします。

メソッドの引数にrefやoutなどのパラメータ修飾子が付けられているならば、parametersに渡す引数をローカルで保持しておきます。c# - How to pass a parameter as a reference with MethodInfo.Invoke - Stack Overflow

int a = 1;
object[] parameters = new object[] { a };

method1.Invoke(obj, parameters);
// 値をコピーして渡しているためaには作用しないが、parameters[0]から結果を得られる

このように呼び出したメソッドからはすべての例外がTargetInvocationExceptionで投げられるため、実際の例外はそれのInnerExceptionで確認します。TargetInvocationException クラス (System.Reflection) | Microsoft Learn

MakeGenericMethod()

ジェネリック メソッドの定義に置き換えたMethodInfoを得ることで、それを呼び出せます。MethodInfo.MakeGenericMethod(Type[]) Method (System.Reflection) | Microsoft Learn

たとえばメソッドが、

public class MyClass
{
    public void Method<T>(T val) {}
}

ように定義されているならば、次のように呼び出せます。

Type type = typeof(MyClass);
MethodInfo method = type.GetMethod("Method");

MyClass obj = new MyClass();
// method.Invoke(obj, new object[] { 1 }); // InvalidOperationException「ContainsGenericParameters が true に設定されている型またはメソッド上で遅延バインディング操作を実行することはできません。」

// obj.Method<int>(1); の呼び出し
MethodInfo genericMethod1 = method.MakeGenericMethod(typeof(int));
genericMethod1.Invoke(obj, new object[] { 1 });

// obj.Method<string>("a"); の呼び出し
MethodInfo genericMethod2 = method.MakeGenericMethod(typeof(string));
genericMethod2.Invoke(obj, new object[] { "a" });
GetProperty()

バインディング制約を指定して、プロパティを検索できます。

public System.Reflection.PropertyInfo GetProperty (
    string name,
    System.Reflection.BindingFlags bindingAttr
    );
GetProperty(String, BindingFlags) - Type.GetProperty メソッド (System) | Microsoft Learn

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

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

typeof演算子 (typeof expression)

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

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

GetTypeメソッド

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

int a = 0;
Type type = a.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

型のサイズの取得

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から検索