反射


反射

定义 :查看和操作元数据的操作 ;

原理: 利用元数据(包括) ,获取类型的完整描述'

特点: 可以获取类型所有的成员和方法,无论他们是否是private,甚至还可以修改值,调用私有静态构造函数。
动态加载(也叫晚期绑定);

使用套路

  class Program
    {
        static void Main(string[] args)
        {
            Person p = new Person();
            Type type = p.GetType();

            //反射可以获取并调用私有方法
            var set_NameMethod = type.GetMethod("set_Name", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic);
            set_NameMethod.Invoke(p, new object[] { "aaa" });

            //当方法带有参数时,需要按顺序传递参数的类型
            var setNickNameMethod = type.GetMethod("SetNickName", new Type[] { typeof(string), typeof(string) });
            setNickNameMethod.Invoke(p, new object[] { "xxx", "jack" });

            //方法二,使用InvokeMember 调用方法不常用; Binder参数不能传Null
            // type.InvokeMember("SetNickName", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic, null, p, new object[] { "xxx", "jim" });

            //调用泛型方法 ,必须要调用  MakeGenericMethod(type)
            var genericMethod = type.GetMethod("SetTemperament");
            var intSetTemperament = genericMethod.MakeGenericMethod(typeof(int));
            intSetTemperament.Invoke(p, new object[] { 100 });

            var strSetTemperament = genericMethod.MakeGenericMethod(typeof(string));
            strSetTemperament.Invoke(p, new object[] { "Nice" });

            //读写实例字段
            var field_NickName = type.GetField("_NickName", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic);
            field_NickName.GetValue(p);
            field_NickName.SetValue(p, "jack");

            //读写静态字段,当是静态字段时,obj参数的值可以传 p 或者 null
            var field_MaxAge = type.GetField("_MaxAge", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic);
            field_MaxAge.GetValue(null);
            field_MaxAge.SetValue(null, 500);

        }
    }

    public class Person
    {
        public string Name { get; private set; }
        private string _NickName = "xxx";
        public static int _MaxAge;

        static Person() { _MaxAge = 200; }

        public void PrintName() { Console.WriteLine(Name); }

        public void SetNickName(string oldName, string newName)
        {
            if (oldName != _NickName)
            {
                Console.WriteLine("旧的 _NickName 不正确"); return;
            }
            _NickName = newName;
        }

        public object _Temperament;
        public void SetTemperament(T temperament)
        {
            Console.WriteLine($"temperament Type:{temperament.GetType()}");
            _Temperament = temperament;
        }
    }

关于 BingingFlags 常用的值

名称 说明
Instance 指定实例成员要包括在搜索中。
Static 指定静态成员要包括在搜索中
NonPublic 指定非公共成员要包括在搜索中。
DeclaredOnly 指定只应考虑在所提供类型的层次结构级别上声明的成员。 不考虑继承的成员。

常用方法:

方法名称 说明
GetMembers() 返回为当前 Type 的所有公共成员
GetMethods() 返回为当前 Type 的所有公共方法。
GetProperties() 返回为当前 Type 的所有公共属性。
GetFields() 返回当前 Type 的所有公共字段。
GetConstructors() 返回为当前 Type 定义的所有公共构造函数。
上面的几个方法 ,都有对应的 MethodName(BindingFlags) 重载版本

性能:
反射调用时比较慢,由于要进行参数校验,和类型对比;大概是直接调用的100倍;

大多数情况下性能:直接调用>委托调用>Emit调用>表达式调用>反射调用>反射后调用DynamicInvoke 。

优化的方法是通过创建代理 ,然后 代理对象.Invoke ,注意是使用delegate.Invoke,而不是delegate.DynamicInvoke;
但是需要创建代理,通用的方案是结合泛型
通过表达式创建委托