OITA: Oika's Information Technological Activities

@oika 情報技術的活動日誌。

.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);  //"鈴木"  

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