パブリックでないメンバやクラスにアクセスする手段として
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); //"鈴木"
あんまり使う人もいないだろうか。
でもテスト書いてるとやっぱ時々リフレクション便利だなーって場面があるんすよね。