値型は、データを直接格納します。
特定の値型に対して、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
参照型は、データへの参照を格納します。
単純型とは、コンパイラが直接サポートするデータ型です。この型は既存の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ビット 浮動小数点型 |
型を明示せず、その決定をコンパイラに委ねます。
var i = 10;
コンパイラによる型チェックを回避できる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' に変換できません
派生型への変換など、実行時に型変換に失敗すると例外が投げられます。
object o = new object(); int i1 = (int)o; // System.InvalidCastException:指定されたキャストは有効ではありません。(Specified cast is not valid.) int i = 10; object o1 = i; // ok
変換可能ならば変換し、不可能ならば例外を投げずに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 クラス (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
バインディング制約を指定して、コンストラクタを検索できます。
[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);
バインディング制約を指定して、フィールドを検索できます。
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);
バインディング制約を指定して、メソッドを検索できます。
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
ジェネリック メソッドの定義に置き換えた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" });
バインディング制約を指定して、プロパティを検索できます。
public System.Reflection.PropertyInfo GetProperty ( string name, System.Reflection.BindingFlags bindingAttr );GetProperty(String, BindingFlags) - Type.GetProperty メソッド (System) | Microsoft Learn
Typeクラスのオブジェクトは、次の方法により取得できます。
型を指定して取得できます。typeof (C# リファレンス) | MSDN
Type type = typeof(int); // FullName = "System.Int32"
インスタンスから取得できます。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 (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 以降でサポート