型・インスタンスが継承関係にあるかどうか、他の型に代入可能かどうかを判定する方法について。

インスタンスと継承関係・代入可能性の判定

is演算子(C#)・TypeOf演算子(VB)を使うことで、インスタンスが特定の型と同じ型か、もしくは特定の型から派生したものかどうかを判定することができる。

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

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

class Sample {
  static void Main()
  {
    object instClass1 = new Class1();
    object instClass2 = new Class2();
    object instClass3 = new Class3();

    Console.WriteLine("instClass1 is Base = {0}", instClass1 is Base);
    Console.WriteLine("instClass2 is Base = {0}", instClass2 is Base);
    Console.WriteLine("instClass3 is Base = {0}", instClass3 is Base);
    Console.WriteLine("instClass2 is Class1 = {0}", instClass2 is Class1);
    Console.WriteLine("instClass1 is Class2 = {0}", instClass1 is Class2);
  }
}
実行結果
instClass1 is Base = True
instClass2 is Base = True
instClass3 is Base = False
instClass2 is Class1 = True
instClass1 is Class2 = False
インスタンスが特定の型か、あるいはその派生クラスか調べる
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 instClass1 As Object = New Class1()
    Dim instClass2 As Object = New Class2()
    Dim instClass3 As Object = New Class3()

    Console.WriteLine("TypeOf instClass1 Is Base = {0}", TypeOf instClass1 Is Base)
    Console.WriteLine("TypeOf instClass2 Is Base = {0}", TypeOf instClass2 Is Base)
    Console.WriteLine("TypeOf instClass3 Is Base = {0}", TypeOf instClass3 Is Base)
    Console.WriteLine("TypeOf instClass2 Is Class1 = {0}", TypeOf instClass2 Is Class1)
    Console.WriteLine("TypeOf instClass1 Is Class2 = {0}", TypeOf instClass1 Is Class2)
  End Sub
End Class
実行結果
TypeOf instClass1 Is Base = True
TypeOf instClass2 Is Base = True
TypeOf instClass3 Is Base = False
TypeOf instClass2 Is Class1 = True
TypeOf instClass1 Is Class2 = False

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

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

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

class Sample {
  static void Main()
  {
    object instClass1 = new Class1();
    object instClass2 = new Class2();

    Console.WriteLine("instClass1 is IIfc1 = {0}", instClass1 is IIfc1);
    Console.WriteLine("instClass2 is IIfc2 = {0}", instClass2 is IIfc2);
    Console.WriteLine("instClass1 is IIfc2 = {0}", instClass1 is IIfc2);
    Console.WriteLine("instClass2 is IIfc1 = {0}", instClass2 is IIfc1);
  }
}
実行結果
instClass1 is IIfc1 = True
instClass2 is IIfc2 = True
instClass1 is IIfc2 = False
instClass2 is IIfc1 = False
インスタンスが特定のインターフェイスを実装しているか調べる
Imports System

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

Class Sample
  Shared Sub Main()
    Dim instClass1 As Object = New Class1()
    Dim instClass2 As Object = New Class2()

    Console.WriteLine("TypeOf instClass1 Is IIfc1 = {0}", TypeOf instClass1 Is IIfc1)
    Console.WriteLine("TypeOf instClass2 Is IIfc2 = {0}", TypeOf instClass2 Is IIfc2)
    Console.WriteLine("TypeOf instClass1 Is IIfc2 = {0}", TypeOf instClass1 Is IIfc2)
    Console.WriteLine("TypeOf instClass2 Is IIfc1 = {0}", TypeOf instClass2 Is IIfc1)
  End Sub
End Class
実行結果
TypeOf instClass1 Is IIfc1 = True
TypeOf instClass2 Is IIfc2 = True
TypeOf instClass1 Is IIfc2 = False
TypeOf instClass2 Is IIfc1 = False
インスタンスが特定の型の変数に代入可能かどうか調べる
using System;

class Class1 {}

class Sample {
  static void Main()
  {
    object instClass1 = new Class1();

    Console.WriteLine("instClass1 is int = {0}", instClass1 is int);
    Console.WriteLine("instClass1 is object = {0}", instClass1 is object);
  }
}
実行結果
instClass1 is int = False
instClass1 is object = True
インスタンスが特定の型の変数に代入可能かどうか調べる
Imports System

Class Class1 : End Class

Class Sample
  Shared Sub Main()
    Dim instClass1 As Object = New Class1()

    Console.WriteLine("TypeOf instClass1 Is Integer = {0}", TypeOf instClass1 Is Integer)
    Console.WriteLine("TypeOf instClass1 Is Object = {0}", TypeOf instClass1 Is Object)
  End Sub
End Class
実行結果
TypeOf instClass1 Is Integer = False
TypeOf instClass1 Is Object = 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