◆ get/getPrivate : リフレクションでプロパティかフィールドを取得
◆ set/setPrivate : リフレクションでプロパティかフィールドを設定
◆ invoke/invokePrivate : リフレクションでメソッド実行
◆ create : インスタンス生成
◆ getNestedType : インナークラスの型を取得 

private クラスの動作確認したいときや フレームワークやライブラリなど提供されてるものの中を見たいなどリフレクション使う機会っていろいろありますよね

でも C# のリフレクションってちょっとプロパティの値取り出したり メソッド実行したりというだけでも書くことが多くてめんどくさいです
楽に書けるようにしたい ということでエクステンションクラスを作ってみました


前回にもリフレクションのちょっとしたエクステンション作ったのですが あれは使い方が特殊すぎたので今回は一般的な使い方できるものです

前回の参考:
var value = new SampleClass().eval(".prop1.method().prop2");
var val = new SampleClass().eval("method({arg})", new { arg = new[] { 1 } })

……見るからに特殊ですよね


今回のは get にプロパティ名入れて取得 set にプロパティ名と値を入れて代入 invoke にメソッド名と引数渡して実行 という感じです

サンプル

サンプル用のクラスを適当につくります
namespace samproj
{
    public class Class1
    {
        private int _num = 5;
        public int num
        {
            get { return this._num; }
            set
            {
                this._num = value;
                this.str += value;
            }
        }
        private string str { get; set; }
        protected bool flag = true;
        public Class1(string str)
        {
            this.str = str;
        }

        public class T1 {
            private static T2 make()
            {
                return new T2();
            }
        }
        private class T2
        {
            private int val = 0;
            public void set(int a)
            {
                this.val = a;
            }
            private void set()
            {
                this.val = 0;
            }
        }
    }
}

このクラスをリフレクションで操作します
var type = Type.GetType("samproj.Class1, samproj");

var inst = type.create(new object[] { "abcd" });
var num = inst.get("num");
// 5
var _num = inst.getPrivate("_num");
// 5
var str = inst.getPrivate("str");
// abcd
var flag = inst.getPrivate("flag");
// true

こんな感じで値をとれます
public でないものは private 付きメソッドを使います
protected でも internal でも public でなくてアクセスできないものを取るときには private メソッドです

プロパティとフィールドの区別はありません

次はセットします
inst.set("num", 20);
inst.setPrivate("_num", 30);
inst.setPrivate("flag", false);

var num2 = inst.get("num");
// 30
var _num2 = inst.getPrivate("_num");
// 30
var str2 = inst.getPrivate("str");
// abcd20
var flag2 = inst.getPrivate("flag");
// false

セットも同じで set/setPrivate を使います
get するとちゃんと値が変わっていますね


インナークラスは getInnerType で取得します
public ならリフレクションするまでもないので デフォルトで non public も取れるようになっています
var t1 = type.getInnerType("T1");
var t2ig = t1.invokePrivate("make", null);
// T2 {}

var t2 = type.getInnerType("T2");
var t2i = t2.create(null);
t2i.invokePrivate("set", new object[] { 100 });
var val = t2i.getPrivate("val");
// 100
t2i.invokePrivate("set", null);
var val2 = t2i.getPrivate("val");
// 0

メソッド呼び出しは invoke/invokePrivate です
MethodInfo をいったん取得しないので引数に応じてオーバーロードも自動解決してくれます


invoke/invokePrivate などの private のありなしですが private 付きメソッドは non-public 「」 対象にするのではなく non-public 「」 対象にする です
なので常に public も protected も private も全部対象にしたいなら全部 private 付きメソッドでおっけいです
var num_o1 = inst.getPrivate("num");
var num_o2 = inst.getPrivate("_num");

また public はそのプロパティなどのアクセス修飾子だけを見ます
T2 は private クラスですが その public メソッドは invoke で実行できます
t2i.invoke("set", new object[] { 200 });
invokePrivate でなくてもおっけいです

private を private なしのメソッドで扱おうとした場合は プロパティやメソッドなどが見つからないので例外が発生します
存在しないプロパティ名を指定したのと一緒です
var flag_x = inst.get("flag");
// RuntimeReflectionException

コード

using System;
using System.Reflection;

namespace liblib
{
    public static class ReflectionExtension
    {
        /// <summary>
        /// ReflectionExtension でのエラー
        /// </summary>
        public class RuntimeReflectionException : Exception
        {
            public RuntimeReflectionException(string message) : base(message) { }
        }

        /// <summary>
        /// リフレクションでプロパティ/フィールドを取得
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="name"></param>
        /// <param name="instance"></param>
        /// <param name="only_public"></param>
        /// <returns></returns>
        public static object getCommon(Type type, string name, object instance, bool only_public)
        {
            var flags = BindingFlags.GetProperty | BindingFlags.GetField | BindingFlags.Public
                | (instance == null ? BindingFlags.Static : BindingFlags.Instance)
                | (only_public ? 0 : BindingFlags.NonPublic);
            var pinfo = type.GetProperty(name, flags);
            if (pinfo != null)
            {
                return pinfo.GetValue(instance);
            }
            var finfo = type.GetField(name, flags);
            if (finfo != null)
            {
                return finfo.GetValue(instance);
            }
            throw new RuntimeReflectionException($"'{name}' field or property not found.");
        }

        public static object get(this object obj, string name)
        {
            return getCommon(obj.GetType(), name, obj, true);
        }

        public static object getPrivate(this object obj, string name)
        {
            return getCommon(obj.GetType(), name, obj, false);
        }

        public static object get(this Type type, string name)
        {
            return getCommon(type, name, null, true);
        }

        public static object getPrivate(this Type type, string name)
        {
            return getCommon(type, name, null, false);
        }

        /// <summary>
        /// リフレクションでプロパティ/フィールドを設定
        /// </summary>
        /// <param name="type"></param>
        /// <param name="name"></param>
        /// <param name="instance"></param>
        /// <param name="value"></param>
        /// <param name="only_public"></param>
        public static void setCommon(Type type, string name, object instance, object value, bool only_public)
        {
            var flags = BindingFlags.GetProperty | BindingFlags.GetField | BindingFlags.Public
                | (instance == null ? BindingFlags.Static : BindingFlags.Instance)
                | (only_public ? 0 : BindingFlags.NonPublic);
            var pinfo = type.GetProperty(name, flags);
            if (pinfo != null)
            {
                pinfo.SetValue(instance, value);
                return;
            }
            var finfo = type.GetField(name, flags);
            if (finfo != null)
            {
                finfo.SetValue(instance, value);
                return;
            }
            throw new RuntimeReflectionException($"'{name}' field or property not found.");
        }

        public static void set(this object obj, string name, object value)
        {
            setCommon(obj.GetType(), name, obj, value, true);
        }

        public static void setPrivate(this object obj, string name, object value)
        {
            setCommon(obj.GetType(), name, obj, value, false);
        }

        public static void set(this Type type, string name, object value)
        {
            setCommon(type, name, null, value, true);
        }

        public static void setPrivate(this Type type, string name, object value)
        {
            setCommon(type, name, null, value, false);
        }

        /// <summary>
        /// リフレクションでメソッド実行
        /// </summary>
        /// <param name="type"></param>
        /// <param name="name"></param>
        /// <param name="instance"></param>
        /// <param name="args"></param>
        /// <param name="only_public"></param>
        /// <returns></returns>
        public static object invokeCommon(Type type, string name, object instance, object[] args, bool only_public)
        {
            var flags = BindingFlags.InvokeMethod | BindingFlags.Public
                | (instance == null ? BindingFlags.Static : BindingFlags.Instance)
                | (only_public ? 0 : BindingFlags.NonPublic);
            return type.InvokeMember(name, flags, null, instance, args);
        }

        public static object invoke(this object obj, string name, object[] args)
        {
            return invokeCommon(obj.GetType(), name, obj, args, true);
        }

        public static object invokePrivate(this object obj, string name, object[] args)
        {
            return invokeCommon(obj.GetType(), name, obj, args, false);
        }

        public static object invoke(this Type type, string name, object[] args)
        {
            return invokeCommon(type, name, null, args, true);
        }

        public static object invokePrivate(this Type type, string name, object[] args)
        {
            return invokeCommon(type, name, null, args, false);
        }

        public static object create(this Type type, object[] args)
        {
            return Activator.CreateInstance(type, args);
        }

        public static Type getInnerType(this Type type, string name)
        {
            var flags = BindingFlags.Public | BindingFlags.NonPublic;
            return type.GetNestedType(name, flags);
        }
    }
}
Gist