インスタンスや型が他の型に代入可能かどうか、特定のインターフェイスを実装しているか、別の型と継承関係にあるかどうかを判定する方法(型チェック・タイプチェック)について。

既存のインスタンスに対する判定is演算子(C#)・TypeOf演算子(VB)、型情報に対する判定はTypeクラスを使って判定する。

インスタンスに対する判定

あるインスタンスに対して特定の型との関係を判定するには、C#ではis演算子・VBではTypeOf演算子を使う。

is演算子はinstance is typenameTypeOf演算子はIsを伴ってTypeOf instance Is typenameのような構文として使う。 どちらの演算子も、instanceの型が次のいずれかを満たす場合にtrueとなる。

typenameと同一の型である
§.同一性の判定
typenameから派生した型である
§.継承関係の判定
typenameと互換性・代入可能性がある
(typename型のインターフェイスを実装している・typename型の変数に代入できる)
§.互換性・代入可能性の判定

これに加えて、is演算子では次の場合もtrueとなる。

typenameが型名ではなくnull、かつinstancenullである
§.等価演算子によらないnull判定

TypeOf演算子はIsNotと共に使うこともできる。 つまり、TypeOf instance IsNot typenameのような構文として使う。 演算子の結果は上記の場合を反転(Not)したものとなる。

C#のtypeof演算子とVBのTypeOf演算子は対応関係にはないので注意。 C#のtypeof演算子は、特定の型の型情報(System.Type)を取得する目的で使う。 これに相当するVBの演算子はGetType演算子となる。

一方、C#のnameof演算子とVBのNameOf演算子は、ともに型・変数・メンバの名前を取得する演算子となっていて、こちらは対応関係にあるので注意。

同一性の判定

is演算子・TypeOf演算子では、あるインスタンスの型が特定の型と同一であるかを判定することができる。

object変数の値が特定の型と同一かどうか調べる
using System;

class Sample {
  static void Main()
  {
    object n = 1; // int

    Console.WriteLine($"n is int = {n is int}");
    Console.WriteLine($"n is long = {n is long}");
    Console.WriteLine($"n is double = {n is double}");
  }
}
実行結果
n is int = True
n is long = False
n is double = False
object変数の値が特定の型と同一かどうか調べる
Imports System

Class Sample
  Shared Sub Main()
    Dim n As Object = 1 ' Integer

    Console.WriteLine($"TypeOf n Is Integer = {TypeOf n Is Integer}")
    Console.WriteLine($"TypeOf n Is Long = {TypeOf n Is Long}")
    Console.WriteLine($"TypeOf n Is Double = {TypeOf n is Double}")
  End Sub
End Class
実行結果
n is int = True
n is long = False
n is double = False

インスタンスが特定の型と同一かどうか調べる
using System;

class C1 {}
class C2 {}
class C3 {}

class Sample {
  static void Main()
  {
    object o = new C3();

    Console.WriteLine($"o is C1 = {o is C1}");
    Console.WriteLine($"o is C2 = {o is C2}");
    Console.WriteLine($"o is C3 = {o is C3}");
  }
}
実行結果
o is C1 = False
o is C2 = False
o is C3 = True
インスタンスが特定の型と同一かどうか調べる
Imports System

Class C1 : End Class
Class C2 : End Class
Class C3 : End Class

Class Sample
  Shared Sub Main()
    Dim o As Object = New C3()

    Console.WriteLine($"TypeOf o Is C1 = {TypeOf o Is C1}")
    Console.WriteLine($"TypeOf o Is C2 = {TypeOf o Is C2}")
    Console.WriteLine($"TypeOf o Is C3 = {TypeOf o Is C3}")
  End Sub
End Class
実行結果
TypeOf o Is C1 = False
TypeOf o Is C2 = False
TypeOf o Is C3 = True

継承関係の判定

is演算子・TypeOf演算子では、あるインスタンスの型が特定の型と同一であるかだけでなく、特定の型から派生したものか(継承関係にあるか)判定することができる。

インスタンスが特定の型か、あるいはその派生クラスか調べる
using System;

class C0 {}
class C1 : C0 {}
class C2 : C1 {}

class Sample {
  static void Main()
  {
    object o0 = new C0();
    object o1 = new C1();
    object o2 = new C2();

    Console.WriteLine($"o0 is C0 = {o0 is C0}"); // true: o0(C0)とC0は同一の型であるため
    Console.WriteLine($"o1 is C0 = {o1 is C0}"); // true: o1(C1)はC0から派生した型であるため
    Console.WriteLine($"o2 is C0 = {o2 is C0}"); // true: o2(C2)はC0から直接派生していなくても、基底クラスのC1がC0と継承関係にある型であるため
    Console.WriteLine();

    Console.WriteLine($"o0 is C1 = {o0 is C1}"); // false: o0(C0)はC1と同一でもC1から派生した型でもないため
    Console.WriteLine($"o1 is C1 = {o1 is C1}"); // true: o1(C1)はC1と同一の型であるため
    Console.WriteLine($"o2 is C1 = {o2 is C1}"); // true: o2(C2)はC1から派生した型であるため
  }
}
実行結果
o0 is C0 = True
o1 is C0 = True
o2 is C0 = True

o0 is C1 = False
o1 is C1 = True
o2 is C1 = True
インスタンスが特定の型か、あるいはその派生クラスか調べる
Imports System

Class C0 : End Class
Class C1 : Inherits C0 : End Class
Class C2 : Inherits C1 : End Class

Class Sample
  Shared Sub Main()
    Dim o0 As Object = New C0()
    Dim o1 As Object = New C1()
    Dim o2 As Object = New C2()

    Console.WriteLine($"TypeOf o0 Is C0 = {TypeOf o0 Is C0}") ' True: o0(C0)とC0は同一の型であるため
    Console.WriteLine($"TypeOf o1 Is C0 = {TypeOf o1 Is C0}") ' True: o1(C1)はC0から派生した型であるため
    Console.WriteLine($"TypeOf o2 Is C0 = {TypeOf o2 Is C0}") ' True: o2(C2)はC0から直接派生していなくても、基底クラスのC1がC0と継承関係にある型であるため
    Console.WriteLine()

    Console.WriteLine($"TypeOf o0 Is C1 = {TypeOf o0 Is C1}") ' False: o0(C0)はC1と同一でもC1から派生した型でもないため
    Console.WriteLine($"TypeOf o1 Is C1 = {TypeOf o1 Is C1}") ' True: o1(C1)はC1と同一の型であるため
    Console.WriteLine($"TypeOf o2 Is C1 = {TypeOf o2 Is C1}") ' True: o2(C2)はC1から派生した型であるため
  End Sub
End Class
実行結果
TypeOf o0 Is C0 = True
TypeOf o1 Is C0 = True
TypeOf o2 Is C0 = True

TypeOf o0 Is C1 = False
TypeOf o1 Is C1 = True
TypeOf o2 Is C1 = True

互換性・代入可能性の判定

型の継承関係ではなく、インスタンスが特定のインターフェイスを実装しているか、またインスタンスが特定の型の変数に代入可能かどうか(型に互換性・代入可能性があるか)を判定したい場合にもis演算子(C#)・TypeOf演算子(VB)を使うことが出来る。

インスタンスが特定のインターフェイスを実装しているか調べる
using System;
using System.Collections.Generic;

interface I1 {}
interface I2 {}
interface I3 : I1, I2 {}

class C1 : I1 {}
class C2 : I1, I2 {}
class C3 : I3 {}

class Sample {
  static void Main()
  {
    object o1 = new C1();
    object o2 = new C2();
    object o3 = new C3();

    Console.WriteLine($"o1 is I1 = {o1 is I1}"); // true: o1(C1)はインターフェイスI1を実装しているため
    Console.WriteLine($"o1 is I2 = {o1 is I2}"); // false: o1(C1)はインターフェイスI2を実装していないため
    Console.WriteLine();

    Console.WriteLine($"o2 is I1 = {o2 is I1}"); // true: o2(C2)はインターフェイスI1を実装しているため
    Console.WriteLine($"o2 is I2 = {o2 is I2}"); // true: o2(C2)はインターフェイスI2を実装しているため
    Console.WriteLine();

    Console.WriteLine($"o3 is I1 = {o3 is I1}"); // true: o3(C3)はインターフェイスI1を継承したI3を実装しているため
    Console.WriteLine($"o3 is I2 = {o3 is I2}"); // true: o3(C3)はインターフェイスI2を継承したI3を実装しているため
    Console.WriteLine();

    var arr = new int[0];

    Console.WriteLine($"arr is IEnumerable<int> = {arr is IEnumerable<int>}"); // true: arrはIEnumerable<int>を実装しているため
    Console.WriteLine($"arr is IEnumerable<long> = {arr is IEnumerable<long>}"); // false: arrはIEnumerable<long>を実装していないため
    Console.WriteLine($"arr is IReadOnlyList<int> = {arr is IReadOnlyList<int>}"); // true: arrはIReadOnlyList<int>を実装しているため
  }
}
実行結果
o1 is I1 = True
o1 is I2 = False

o2 is I1 = True
o2 is I2 = True

o3 is I1 = True
o3 is I2 = True

arr is IEnumerable<int> = True
arr is IEnumerable<long> = False
arr is IReadOnlyList<int> = True
インスタンスが特定のインターフェイスを実装しているか調べる
Imports System
Imports System.Collections.Generic

Interface I1 : End Interface
Interface I2 : End Interface
Interface I3 : Inherits I1, I2 : End Interface

Class C1 : Implements I1 : End Class
class C2 : Implements I1, I2 : End Class
class C3 : Implements I3 : End Class

Class Sample
  Shared Sub Main()
    Dim o1 As Object = New C1()
    Dim o2 As Object = New C2()
    Dim o3 As Object = New C3()

    Console.WriteLine($"TypeOf o1 Is I1 = {TypeOf o1 Is I1}") ' True: o1(C1)はインターフェイスI1を実装しているため
    Console.WriteLine($"TypeOf o1 Is I2 = {TypeOf o1 Is I2}") ' False: o1(C1)はインターフェイスI2を実装していないため
    Console.WriteLine()

    Console.WriteLine($"TypeOf o2 Is I1 = {TypeOf o2 Is I1}") ' True: o2(C2)はインターフェイスI1を実装しているため
    Console.WriteLine($"TypeOf o2 Is I2 = {TypeOf o2 Is I2}") ' True: o2(C2)はインターフェイスI2を実装しているため
    Console.WriteLine()

    Console.WriteLine($"TypeOf o3 Is I1 = {TypeOf o3 Is I1}") ' True: o3(C3)はインターフェイスI1を継承したI3を実装しているため
    Console.WriteLine($"TypeOf o3 Is I2 = {TypeOf o3 Is I2}") ' True: o3(C3)はインターフェイスI2を継承したI3を実装しているため
    Console.WriteLine()

    Dim arr = New Integer() {}

    Console.WriteLine($"TypeOf arr Is IEnumerable(Of Integer) = {TypeOf arr Is IEnumerable(Of Integer)}") ' True: arrはIEnumerable(Of Integer)を実装しているため
    Console.WriteLine($"TypeOf arr Is IEnumerable(Of Long) = {TypeOf arr Is IEnumerable(Of Long)}") ' False: arrはIEnumerable(Of Long)を実装していないため
    Console.WriteLine($"TypeOf arr Is IReadOnlyList(Of Integer) = {TypeOf arr Is IReadOnlyList(Of Integer)}") ' True: arrはIReadOnlyList(Of Integer)を実装しているため
  End Sub
End Class
実行結果
TypeOf o1 Is I1 = True
TypeOf o1 Is I2 = False

TypeOf o2 Is I1 = True
TypeOf o2 Is I2 = True

TypeOf o3 Is I1 = True
TypeOf o3 Is I2 = True

TypeOf arr Is IEnumerable(Of Integer) = True
TypeOf arr Is IEnumerable(Of Long) = False
TypeOf arr Is IReadOnlyList(Of Integer) = True

特定の型の変数に代入可能かどうか(型に互換性・代入可能性があるか)は、より厳密に言えば単純に変数に代入できるかどうかとは異なる。 ここで言う互換性・代入可能性とは、インターフェイスに対する互換性・代入可能性となる。

そのため、型変換を伴う必要がある場合、例えばintshort, int→int型のenumなど明示的な型変換(縮小変換)はもちろん、intlongなど暗黙の型変換(拡大変換)が必要になる型の場合も、互換性・代入可能性がないものとして扱われる。

インスタンスが特定の型と互換性・代入可能性があるかどうか調べる
using System;

enum E : int {} // int型列挙体

class Sample {
  static void Main()
  {
    object n = 0; // int

    Console.WriteLine($"n is int = {n is int}");       // true: nはintであるため
    Console.WriteLine($"n is object = {n is object}"); // true: n(int)はobjectと互換性(継承関係)があるため (Int32->ValueType->Object)
    Console.WriteLine($"n is IConvertible = {n is IConvertible}"); // true: intはIConvertibleと互換性(インターフェイス実装)があるため
    Console.WriteLine($"n is long = {n is long}");     // false: n(int)はlong型変数に*代入は*できるが、暗黙の型変換(拡大変換)を行う必要があるため
    Console.WriteLine($"n is E = {n is E}");           // false: n(int)はE(int型列挙体)に*代入は*できるが、明示的な型変換(縮小変換)を行う必要があるため
  }
}
実行結果
n is int = True
n is object = True
n is IConvertible = True
n is long = False
n is E = False
インスタンスが特定の型と互換性・代入可能性があるかどうか調べる
Imports System

Enum E As Integer ' Integer型列挙体
  E1
End Enum

Class Sample
  Shared Sub Main()
    Dim n As Object = 0 ' Integer

    Console.WriteLine($"TypeOf n Is Integer = {TypeOf n Is Integer}") ' True: nはIntegerであるため
    Console.WriteLine($"TypeOf n Is Object = {TypeOf n Is Integer}")  ' True: n(Integer)はObjectと互換性(継承関係)があるため (Int32->ValueType->Object)
    Console.WriteLine($"TypeOf n Is IConvertible = {TypeOf n Is IConvertible}") ' True: IntegerはIConvertibleと互換性(インターフェイス実装)があるため
    Console.WriteLine($"TypeOf n Is Long = {TypeOf n Is Long}")       ' False: n(Integer)はLong型変数に*代入は*できるが、暗黙の型変換(拡大変換)を行う必要があるため
    Console.WriteLine($"TypeOf n Is E = {TypeOf n Is E}")             ' False: n(Integer)はE(Integer型列挙体)に*代入は*できるが、明示的な型変換(縮小変換)を行う必要があるため
  End Sub
End Class
実行結果
TypeOf n Is Integer = True
TypeOf n Is Object = True
TypeOf n Is IConvertible = True
TypeOf n Is Long = False
TypeOf n Is E = False

ヌル許容型・ヌル非許容型との判定

ヌル非許容型Tとヌル許容型T?(Nullable<T>)の場合、例えばintint?またはその逆int?intなどの場合、非ヌルの値(値を持っている場合)に対してはヌル非許容型・許容型ともに互換性・代入可能性があるものとして扱われる。 一方、ヌル(値を持っていない場合)に対してはヌル非許容型・許容型ともに互換性・代入可能性がないものとして扱われる。 (ヌル許容型 §.ヌル許容型と型情報)

インスタンスがヌル非許容型・ヌル許容型と互換性・代入可能性があるか、値がヌルかどうか調べる
using System;

class Sample {
  static void Main()
  {
    object n = 0; // int(ヌル非許容型)

    Console.WriteLine($"n is int = {n is int}");    // true: n(=int)はintであるため
    Console.WriteLine($"n is int? = {n is int?}");  // true: n(=int)はint?(Nullable<int>)と互換性があるため
    Console.WriteLine();

    object n_hasval = (int?)0; // 値を持っているヌル許容型

    Console.WriteLine($"n_hasval is int = {n_hasval is int}");    // true: n_hasval(=int)はintであるため
    Console.WriteLine($"n_hasval is int? = {n_hasval is int?}");  // true: n_hasval(=int)はint?(Nullable<int>)と互換性があるため
    Console.WriteLine($"n_hasval is null = {n_hasval is null}");  // false: n_hasval(=int)は値を持っている(=非null)であるため
    Console.WriteLine();

    object n_null = (int?)null; // 値を持っていないヌル許容型

    Console.WriteLine($"n_null is int = {n_null is int}");    // false: n_null(=null)はintではないため
    Console.WriteLine($"n_null is int? = {n_null is int?}");  // false: n_null(=null)はint?ではないため
    Console.WriteLine($"n_null is null = {n_null is null}");  // true: n_null(=null)は値を持っていない(=null)であるため
  }
}
実行結果
n is int = True
n is int? = True

n_hasval is int = True
n_hasval is int? = True
n_hasval is null = False

n_null is int = False
n_null is int? = False
n_null is null = True
インスタンスがヌル非許容型・ヌル許容型と互換性・代入可能性があるか、値がヌルかどうか調べる
Imports System

Class Sample
  Shared Sub Main()
    Dim n As Object = 0 ' Integer(ヌル非許容型)

    Console.WriteLine($"TypeOf n Is Integer = {TypeOf n Is Integer}")   ' true: n(=Integer)はIntegerであるため
    Console.WriteLine($"TypeOf n Is Integer? = {TypeOf n Is Integer?}") ' true: n(=Integer)はInteger?/Nullable(Of Integer)と互換性があるため
    Console.WriteLine()

    Dim n_hasval As Object = CType(0, Integer?) ' 値を持っているヌル許容型

    Console.WriteLine($"TypeOf n_hasval Is Integer = {TypeOf n_hasval Is Integer}")   ' true: n_hasval(=Integer)はIntegerであるため
    Console.WriteLine($"TypeOf n_hasval Is Integer? = {TypeOf n_hasval Is Integer?}") ' true: n_hasval(=Integer)はInteger?/Nullable(Of Integer)と互換性があるため
    Console.WriteLine($"n_hasval Is Nothing = {n_hasval Is Nothing}")   ' false: n_hasval(=Integer)は値を持っている(=非Nothing)であるため
    Console.WriteLine()

    Dim n_nothing As Object = CType(Nothing, Integer?) ' 値を持っていないヌル許容型

    Console.WriteLine($"TypeOf n_nothing Is Integer = {TypeOf n_nothing Is Integer}")    ' false: n_nothing(=Nothing)はIntegerではないため
    Console.WriteLine($"TypeOf n_nothing Is Integer? = {TypeOf n_nothing Is Integer?}")  ' false: n_nothing(=Nothing)はInteger?ではないため
    Console.WriteLine($"n_nothing Is Nothing = {n_nothing Is Nothing}")  ' true: n_nothing(=Nothing)は値を持っていない(=Nothing)であるため
  End Sub
End Class
実行結果
TypeOf n Is Integer = True
TypeOf n Is Integer? = True

TypeOf n_hasval Is Integer = True
TypeOf n_hasval Is Integer? = True
n_hasval Is Nothing = False

TypeOf n_nothing Is Integer = False
TypeOf n_nothing Is Integer? = False
n_nothing Is Nothing = True

このように、is演算子・TypeOf演算子(Is演算子)では、インスタンスが非ヌルの場合はその型がヌル許容型かヌル非許容型かどうかを区別して判定することはできず、インスタンスがヌルか非ヌルかどうかしか判断することができない。

上記のサンプルのように、C#では、instance is nullのようにis演算子でnullかどうかの判定ができる。 VBでは、TypeOf instance Is Nothingのように型にNothingを指定することはできないので、Is演算子のみを使ってinstance Is NothingのようにすることでNothingかどうかの判定ができる。 (後述:§.等価演算子によらないnull判定)

等価演算子によらないnull判定

C#のis演算子では、インスタンスがnullかどうかを判定することができる。 VBではTypeOf演算子ではなく、Is演算子単体を用いることで、インスタンスがNothingかどうかを判定することができる。

C#では通常、等価演算子==を使うことでnullとの比較を行う。 しかし、等価演算子をオーバーロードすることによって、(意図的あるいは意図せずに)nullとの比較の動作を変えることもできる。 こういった場合は、等価演算子==の代わりにis演算子を用いることにより、インスタンスがnullかどうかの判定を確実に行うことができる。

また、このような場合はis演算子だけでなく、2つのインスタンスが同一かどうかを調べるメソッドObject.ReferenceEqualsを用いることもできる。 引数のどちらかにnull/Nothingを指定すれば、インスタンスがヌルか非ヌルかを調べることができる。

等価演算子がオーバーロードされている型に対して、インスタンスがnullかどうかを調べる
using System;

class C {
  // 等価演算子をオーバーロードする (ここでは左項・右項にかかわらず常にtrueを返す)
  public static bool operator == (C cx, C cy) => true;
  public static bool operator != (C cx, C cy) => !(cx == cy);
}

class Sample {
  static void Main()
  {
    var c1 = new C();
    var c2 = (C)null;

    Console.WriteLine($"c1 == null: {c1 == null}"); // true: 等価演算子==が常にtrueとなるため
    Console.WriteLine($"c1 is null: {c1 is null}"); // false: c1はnullではないため
    Console.WriteLine($"Object.ReferenceEquals(c1, null): {Object.ReferenceEquals(c1, null)}"); // false: c1はnullではないため
    Console.WriteLine();

    Console.WriteLine($"c2 == null: {c2 == null}"); // true: 等価演算子==が常にtrueとなるため
    Console.WriteLine($"c2 is null: {c2 is null}"); // true: c2はnullであるため
    Console.WriteLine($"Object.ReferenceEquals(c2, null): {Object.ReferenceEquals(c2, null)}"); // true: c2はnullであるため
  }
}
実行結果
c1 == null: True
c1 is null: False
Object.ReferenceEquals(c1, null): False

c2 == null: True
c2 is null: True
Object.ReferenceEquals(c2, null): True

型情報に対する判定

継承関係・代入可能性の判定

TypeクラスIsSubclassOfメソッドを使うことで、型情報により型が特定の型から派生するものかどうかを判定できる。 ただし、IsSubclassOfメソッドは同じ型を表す場合はFalseを返す点に注意が必要で、完全に同じ型を表すかどうかを判定する場合はType.Equalsメソッドを用いて判定する必要がある。

型情報から型が特定の型の派生型かどうか調べる
using System;

class Base {}
class Class1 : Base {}
class Class2 : Class1 {}
class Class3 {}

class Sample {
  static void Main()
  {
    Type typeOfBase = typeof(Base);
    Type typeOfClass1 = typeof(Class1);
    Type typeOfClass2 = typeof(Class2);
    Type typeOfClass3 = typeof(Class3);

    Console.WriteLine("{0}.IsSubclassOf({1}) = {2}", typeOfClass1, typeOfBase, typeOfClass1.IsSubclassOf(typeOfBase));
    Console.WriteLine("{0}.IsSubclassOf({1}) = {2}", typeOfClass2, typeOfBase, typeOfClass2.IsSubclassOf(typeOfBase));
    Console.WriteLine("{0}.IsSubclassOf({1}) = {2}", typeOfClass3, typeOfBase, typeOfBase.IsSubclassOf(typeOfClass1));
    Console.WriteLine("{0}.IsSubclassOf({1}) = {2}", typeOfBase, typeOfClass1, typeOfBase.IsSubclassOf(typeOfClass1));
    Console.WriteLine("{0}.IsSubclassOf({1}) = {2}", typeOfBase, typeOfBase, typeOfBase.IsSubclassOf(typeOfBase));
  }
}
型情報から型が特定の型の派生型かどうか調べる
Imports System

Class Base : End Class
Class Class1 : Inherits Base : End Class
Class Class2 : Inherits Class1 : End Class
Class Class3 : End Class

Class Sample
  Shared Sub Main()
    Dim typeOfBase As Type = GetType(Base)
    Dim typeOfClass1 As Type = GetType(Class1)
    Dim typeOfClass2 As Type = GetType(Class2)
    Dim typeOfClass3 As Type = GetType(Class3)

    Console.WriteLine("{0}.IsSubclassOf({1}) = {2}", typeOfClass1, typeOfBase, typeOfClass1.IsSubclassOf(typeOfBase))
    Console.WriteLine("{0}.IsSubclassOf({1}) = {2}", typeOfClass2, typeOfBase, typeOfClass2.IsSubclassOf(typeOfBase))
    Console.WriteLine("{0}.IsSubclassOf({1}) = {2}", typeOfClass3, typeOfBase, typeOfBase.IsSubclassOf(typeOfClass1))
    Console.WriteLine("{0}.IsSubclassOf({1}) = {2}", typeOfBase, typeOfClass1, typeOfBase.IsSubclassOf(typeOfClass1))
    Console.WriteLine("{0}.IsSubclassOf({1}) = {2}", typeOfBase, typeOfBase, typeOfBase.IsSubclassOf(typeOfBase))
  End Sub
End Class
実行結果
Class1.IsSubclassOf(Base) = True
Class2.IsSubclassOf(Base) = True
Class3.IsSubclassOf(Base) = False
Base.IsSubclassOf(Class1) = False
Base.IsSubclassOf(Base) = False

型が特定の型と同じか、あるいは特定の型から派生するかを判定するにはType.IsAssignableFromメソッドを使うことができる。 このメソッドは型が特定の型に代入可能かどうかを判定することができる。 そのため、型が特定のインターフェイスを実装しているかどうかについてもこのメソッドで調べることができる。

型情報から型の代入可能性を調べる
using System;

interface IIfc1 {}
interface IIfc2 {}
class Class1 : IIfc1 {}
class Class2 : IIfc2 {}
class Class3 : Class1, IIfc2 {}

class Sample {
  static void Main()
  {
    Type typeOfIfc1 = typeof(IIfc1);
    Type typeOfIfc2 = typeof(IIfc2);
    Type typeOfClass1 = typeof(Class1);
    Type typeOfClass2 = typeof(Class2);
    Type typeOfClass3 = typeof(Class3);

    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfIfc1, typeOfClass1, typeOfIfc1.IsAssignableFrom(typeOfClass1));
    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfIfc2, typeOfClass1, typeOfIfc2.IsAssignableFrom(typeOfClass1));
    Console.WriteLine();

    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfIfc1, typeOfClass2, typeOfIfc1.IsAssignableFrom(typeOfClass2));
    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfIfc2, typeOfClass2, typeOfIfc2.IsAssignableFrom(typeOfClass2));
    Console.WriteLine();

    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfIfc1, typeOfClass3, typeOfIfc1.IsAssignableFrom(typeOfClass3));
    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfIfc2, typeOfClass3, typeOfIfc2.IsAssignableFrom(typeOfClass3));
    Console.WriteLine();

    Console.WriteLine("{0}.IsSubclassOf({1}) = {2}", typeOfClass3, typeOfClass1, typeOfClass3.IsSubclassOf(typeOfClass1));
    Console.WriteLine("{0}.IsSubclassOf({1}) = {2}", typeOfClass3, typeOfIfc1, typeOfClass3.IsSubclassOf(typeOfIfc1));
    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfIfc1, typeOfClass3, typeOfIfc1.IsAssignableFrom(typeOfClass3));
    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfIfc2, typeOfClass3, typeOfIfc2.IsAssignableFrom(typeOfClass3));
    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfClass1, typeOfClass3, typeOfClass1.IsAssignableFrom(typeOfClass3));
  }
}
型情報から型の代入可能性を調べる
Imports System

Interface IIfc1 : End Interface
Interface IIfc2 : End Interface
Class Class1 : Implements IIfc1 : End Class
Class Class2 : Implements IIfc2 : End Class
Class Class3 : Inherits Class1 : Implements IIfc2 : End Class

Class Sample
  Shared Sub Main()
    Dim typeOfIfc1 As Type = GetType(IIfc1)
    Dim typeOfIfc2 As Type = GetType(IIfc2)
    Dim typeOfClass1 As Type = GetType(Class1)
    Dim typeOfClass2 As Type = GetType(Class2)
    Dim typeOfClass3 As Type = GetType(Class3)

    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfIfc1, typeOfClass1, typeOfIfc1.IsAssignableFrom(typeOfClass1))
    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfIfc2, typeOfClass1, typeOfIfc2.IsAssignableFrom(typeOfClass1))
    Console.WriteLine()

    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfIfc1, typeOfClass2, typeOfIfc1.IsAssignableFrom(typeOfClass2))
    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfIfc2, typeOfClass2, typeOfIfc2.IsAssignableFrom(typeOfClass2))
    Console.WriteLine()

    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfIfc1, typeOfClass3, typeOfIfc1.IsAssignableFrom(typeOfClass3))
    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfIfc2, typeOfClass3, typeOfIfc2.IsAssignableFrom(typeOfClass3))
    Console.WriteLine()

    Console.WriteLine("{0}.IsSubclassOf({1}) = {2}", typeOfClass3, typeOfClass1, typeOfClass3.IsSubclassOf(typeOfClass1))
    Console.WriteLine("{0}.IsSubclassOf({1}) = {2}", typeOfClass3, typeOfIfc1, typeOfClass3.IsSubclassOf(typeOfIfc1))
    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfIfc1, typeOfClass3, typeOfIfc1.IsAssignableFrom(typeOfClass3))
    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfIfc2, typeOfClass3, typeOfIfc2.IsAssignableFrom(typeOfClass3))
    Console.WriteLine("{0}.IsAssignableFrom({1}) = {2}", typeOfClass1, typeOfClass3, typeOfClass1.IsAssignableFrom(typeOfClass3))
  End Sub
End Class
実行結果
IIfc1.IsAssignableFrom(Class1) = True
IIfc2.IsAssignableFrom(Class1) = False

IIfc1.IsAssignableFrom(Class2) = False
IIfc2.IsAssignableFrom(Class2) = True

IIfc1.IsAssignableFrom(Class3) = True
IIfc2.IsAssignableFrom(Class3) = True

Class3.IsSubclassOf(Class1) = True
Class3.IsSubclassOf(IIfc1) = False
IIfc1.IsAssignableFrom(Class3) = True
IIfc2.IsAssignableFrom(Class3) = True
Class1.IsAssignableFrom(Class3) = True

IsSubclassOfメソッドとIsAssignableFromメソッドでは引数の渡し方に注意する必要がある。

IsSubclassOfとIsAssignableFromの呼び出し
TX基底型TBaseの派生型かどうかを調べる場合 TX.IsSubclassOf(TBase)
TXからTBaseに代入可能かどうかを調べる場合 TBase.IsAssignableFrom(TX)

基底クラスの取得

基底クラスが具体的にどのような型か実行時まで知り得ない場合は、Type.BaseTypeプロパティを参照することで継承している型の情報を得ることができる。

using System;

class Base {}
class Class1 : Base {}
class Class2 : Class1 {}
class Class3 {}

class Sample {
  static void Main()
  {
    Console.WriteLine("Class2.BaseType : {0}", typeof(Class2).BaseType);
    Console.WriteLine("Class3.BaseType : {0}", typeof(Class3).BaseType);
    Console.WriteLine();

    // Class2の継承ツリーを遡って表示する
    for (Type type = typeof(Class2); type != null; type = type.BaseType) {
      Console.WriteLine(type);
    }
  }
}
Imports System

Class Base : End Class
Class Class1 : Inherits Base : End Class
Class Class2 : Inherits Class1 : End Class
Class Class3 : End Class

Class Sample
  Shared Sub Main()
    Console.WriteLine("Class2.BaseType : {0}", GetType(Class2).BaseType)
    Console.WriteLine("Class3.BaseType : {0}", GetType(Class3).BaseType)
    Console.WriteLine()

    ' Class2の継承ツリーを遡って表示する
    Dim type As Type = GetType(Class2)

    While Not type Is Nothing
      Console.WriteLine(type)

      type = type.BaseType
    End While
  End Sub
End Class
実行結果
Class2.BaseType : Class1
Class3.BaseType : System.Object

Class2
Class1
Base
System.Object

なお、Object型やインターフェイス型の場合、BaseTypeプロパティはnull/Nothingを返す。

インターフェイスの取得

どのようなインターフェイスを実装しているか実行時まで知り得ない場合は、Type.GetInterfacesメソッドを使用することで型が実装しているインターフェイスすべてを得ることができる。 このメソッドでは、基底クラスで実装されているインターフェイスも返される。

using System;

interface IIfc1 {}
interface IIfc2 {}
class Class1 : IIfc1 {}
class Class2 : IIfc1, IIfc2 {}
class Class3 : Class1, IIfc2 {}

class Sample {
  static void Main()
  {
    Console.WriteLine("interfaces of Class1");
    foreach (Type ifc in typeof(Class1).GetInterfaces()) {
      Console.WriteLine(ifc);
    }
    Console.WriteLine();

    Console.WriteLine("interfaces of Class2");
    foreach (Type ifc in typeof(Class2).GetInterfaces()) {
      Console.WriteLine(ifc);
    }
    Console.WriteLine();

    Console.WriteLine("interfaces of Class3");
    foreach (Type ifc in typeof(Class3).GetInterfaces()) {
      Console.WriteLine(ifc);
    }
    Console.WriteLine();
  }
}
Imports System

Interface IIfc1 : End Interface
Interface IIfc2 : End Interface
Class Class1 : Implements IIfc1 : End Class
Class Class2 : Implements IIfc1, IIfc2 : End Class
Class Class3 : Inherits Class1 : Implements IIfc2 : End Class

Class Sample
  Shared Sub Main()
    Console.WriteLine("interfaces of Class1")
    For Each ifc As Type In GetType(Class1).GetInterfaces()
      Console.WriteLine(ifc)
    Next
    Console.WriteLine()

    Console.WriteLine("interfaces of Class2")
    For Each ifc As Type In GetType(Class2).GetInterfaces()
      Console.WriteLine(ifc)
    Next
    Console.WriteLine()

    Console.WriteLine("interfaces of Class3")
    For Each ifc As Type In GetType(Class3).GetInterfaces()
      Console.WriteLine(ifc)
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
interfaces of Class1
IIfc1

interfaces of Class2
IIfc1
IIfc2

interfaces of Class3
IIfc1
IIfc2

派生クラスの取得

基底クラスの型情報から、そのクラスから派生しているクラスの情報を直接得ることはできない。 そのため派生クラスを取得するには、Assembly.GetTypesメソッドを使ってアセンブリで定義されているすべての型情報を走査し、Type.BaseTypeプロパティを参照して基底クラスが一致するものを1つずつ検索する。

using System;
using System.Reflection;

class Base {}
class Class1 : Base {}
class Class2 : Class1 {}
class Class3 {}

class Sample {
  static void Main()
  {
    // Baseから派生している型を検索する
    Type targetType = typeof(Base);

    // 現在実行しているアセンブリで定義されているすべての型を列挙
    foreach (Type type in Assembly.GetExecutingAssembly().GetTypes()) {
      // 継承ツリーを遡って基底クラスを1つずつ調べる
      for (Type baseType = type.BaseType; baseType != null; baseType = baseType.BaseType) {
        if (baseType == targetType) {
          // 継承ツリーに目的の型と一致する型があった場合、表示する
          Console.WriteLine(type);
          break;
        }
      }
    }
  }
}

Imports System
Imports System.Reflection

Class Base : End Class
Class Class1 : Inherits Base : End Class
Class Class2 : Inherits Class1 : End Class
Class Class3 : End Class

Class Sample
  Shared Sub Main()
    ' Baseから派生している型を検索する
    Dim targetType As Type = GetType(Base)

    ' 現在実行しているアセンブリで定義されているすべての型を列挙
    For Each type As Type In [Assembly].GetExecutingAssembly().GetTypes()
      ' 継承ツリーを遡って基底クラスを1つずつ調べる
      Dim baseType As Type = type.BaseType

      While Not baseType Is Nothing
        If baseType = targetType Then
          ' 継承ツリーに目的の型と一致する型があった場合、表示する
          Console.WriteLine(type)
          Exit While
        End If

        baseType = baseType.BaseType
      End While
    Next
  End Sub
End Class
実行結果
Class1
Class2