[.NET]入れ子になった内部クラスの型をリフレクションで参照する

パブリックでないメンバやクラスにアクセスする手段として
C#ではリフレクションのメソッド群が
System.Reflection名前空間に用意されている。

たとえば、internalなクラスのprivateなメンバに
参照プロジェクトからアクセスするサンプルなこんな感じ。

アクセス先のコード。

namespace OfficeHoge.TeamA.LibA {
    //公開クラス
    public class PublicClass {
    }

    //外部から見えないクラス
    internal class HiddenPerson {

        private string firstName;

        public HiddenPerson(string firstName) {
            this.firstName = firstName;
        }
    }
}

アクセス方法。

//公開クラスからアセンブリを特定
var libAssembly = Assembly.GetAssembly(typeof(OfficeHoge.TeamA.LibA.PublicClass));

//クラス型を取得
var typePerson = libAssembly.GetType("OfficeHoge.TeamA.LibA.HiddenPerson");

//コンストラクタからインスタンス生成
var person = typePerson.GetConstructor(new[] { typeof(string) })
                       .Invoke(new object[] { "一郎" });

//firstNameフィールドの値を取得
var fldFstName = typePerson.GetField("firstName", BindingFlags.Instance | BindingFlags.NonPublic);
var fstName = (string)fldFstName.GetValue(person);

Console.WriteLine(fstName); //"一郎"

 

ここまでは良いとして、本題は、クラス内に入れ子になった
内部クラス(インナークラス)があったときに
リフレクション使って探すのにちょっとつまづいたのでメモ。

ポイントは、クラス型を文字列で指定する際に、
[外部クラス名]+[内部クラス名]というように、”+”で連結すること。
 

例として、先ほどのHiddenPersonクラスの中に
以下のような感じでMotherクラスを作るとする。

namespace OfficeHoge.TeamA.LibA {
    //公開クラス
    public class PublicClass {
    }

    //外部から見えないクラス
    internal class HiddenPerson {

        private Mother mother;
        private string firstName;

        public HiddenPerson(string familyName, string firstName) {
            this.firstName = firstName;
            this.mother = new Mother(familyName);
        }

        //母クラス(内部クラス)
        private class Mother {

            private string familyName;

            public Mother(string familyName) {
                this.familyName = familyName;
            }
        }
    }
}

このMotherのfamilyNameにアクセスしようとする場合、↓こんな感じになる。

//公開クラスからアセンブリを特定
var libAssembly = Assembly.GetAssembly(typeof(OfficeHoge.TeamA.LibA.PublicClass));

//クラス型を取得
var typePerson = libAssembly.GetType("OfficeHoge.TeamA.LibA.HiddenPerson");

//コンストラクタからインスタンス生成
var person = typePerson.GetConstructor(new[] { typeof(string), typeof(string) })
                       .Invoke(new object[] { "鈴木", "一郎" });

//母クラス型を取得
var typeMother = libAssembly.GetType("OfficeHoge.TeamA.LibA.HiddenPerson+Mother");

//motherフィールドのオブジェクトを取得
var fldMother = typePerson.GetField("mother", BindingFlags.Instance | BindingFlags.NonPublic);
var mother = fldMother.GetValue(person);

//familyNameフィールドの値を取得
var fldFamilyName = typeMother.GetField("familyName", BindingFlags.Instance | BindingFlags.NonPublic);
var familyName = (string)fldFamilyName.GetValue(mother);

Console.WriteLine(familyName);  //"鈴木"

あんまり使う人もいないだろうか。
でもテスト書いてるとやっぱ時々リフレクション便利だなーって場面があるんすよね。