やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

指定した型を継承した型の一覧を取得する

 子孫一覧。

成果物

情報源

きっかけ

 こちらでインタフェース継承クラス一覧を取得できると知った。ならば継承クラスを一覧するにはどうすればいいの? と思ったのがきっかけ。

コード

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Aの子のみ");
        foreach (var t in AssemblyUtils.GetChildren<A>()) {Console.WriteLine($"{t}");}
        Console.WriteLine();
        Console.WriteLine("Aの子孫すべて");
        foreach (var t in AssemblyUtils.GetInheritanceTypes<A>()) {Console.WriteLine($"{t}");}
        Console.WriteLine();
        Console.WriteLine("Aの末裔すべて");
        foreach (var t in AssemblyUtils.GetEndInheritanceTypes<A>()) {Console.WriteLine($"{t}");}
    }
}
class A {}
class A1 : A {}
class A2 : A {}
class A11 : A1 {}
class B {}
class B1 : B {}
public static class AssemblyUtils
{
    // 指定した型を継承した子型を返す。(直下の子のみ。子孫以下であっても対象外)
    public static Type[] GetChildren<T>()
    {
        return Assembly.GetExecutingAssembly().GetTypes().Where(c => c.BaseType == typeof(T)).ToArray();
    }
    // 指定した型を継承した子孫型を返す。(指定した型は対象外)
    public static Type[] GetInheritanceTypes<T>()
    {
        return _GetInheritanceTypes<T>().ToArray();
    }
    private static IEnumerable<Type> _GetInheritanceTypes<T>()
    {
        return Assembly.GetExecutingAssembly().GetTypes().Where(c => c.IsSubclassOf(typeof(T)));
    }
    // 指定した型の末裔を返す。(指定した型や末裔以前の型は対象外)
    public static Type[] GetEndInheritanceTypes<T>()
    {
        var types = _GetInheritanceTypes<T>().ToList();
        for (int t1=types.Count-1; t1>=0; t1--) {
            for (int t2=types.Count-1; t2>=0; t2--) {
                if (types[t2] == types[t1].BaseType) { types.RemoveAt(t2); }
            }
        }
        return types.ToArray();
    }
}

出力結果

Aの子のみ
GetInheritanceType.A1
GetInheritanceType.A2

Aの子孫すべて
GetInheritanceType.A1
GetInheritanceType.A2
GetInheritanceType.A11

Aの末裔すべて
GetInheritanceType.A2
GetInheritanceType.A11

 クラスの継承関係は以下。

  • A
    • A1
      • A11
    • A2
  • B
    • B1

学習項目

型の比較

コード 意味
instance is targetType 指定したインタフェースを実装しているか
instance.GetType().IsSubclassOf(targetType) 指定した型から派生した型か(同一ならfalse
instance.GetType().IsAssignableFrom(targetType) 指定した型と同じか派生した型か
- 指定した型を継承した末裔型か

末裔の取得

 指定した型を継承した全型のうち末裔のみ取得したい。どうやるか。

 末裔は、言い換えると「指定した型を継承した全型のうち、どの型からも継承されていない型」である。

  1. 指定した型を継承したすべての型を取得する(_GetInheritanceTypes
  2. 上記から再帰的にBaseTypeを取得し、その一覧を返す
  3. 1の中から2を除外し、残ったものが「どの型からも継承されていない型」である

 ツリー構造になっていないため再帰的にチェックすることはできない。そこでリストをループして網羅的にチェックする。foreachではリストのループ中にリスト項目を削除できない。そこでforを使って行う。削除したときインデックスが狂わないように後ろから順にループする。

所感

 できればツリー構造として返せるようにもしてみたい。

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux