zoukankan      html  css  js  c++  java
  • Reflect(反射)

    反射、反射,程序员的快乐。反射是无处不在的。

    那么什么是反射:通过反射,可以在运行时获得程序或程序集中每一个类型(包括类、结构、委托、接口和枚举等)的成员和成员的信息。有了反射,即可对每一个类型了如指掌

    简单来说,一般来说,我们通过编译生成一个dll文件,dll文件由IL+metadata组成,而我们的反射主要就是调用一些metadata(元数据)。

    我们通过ILSpy反编译可以清楚的看到

    现在我们新建几个类库和接口:

    1、新建一个IDBHelper接口

     public interface IDBHelper
        {
            void Query();
        }
    

    2、在建一个DBHelper类并实现这个接口。

     public class DBHelper : IDBHelper
        {
            public int Id { get; set; }
            public string Name { get; set; }
    
            public DBHelper()
            {
                //Console.WriteLine("这里是{0}的无参构造函数", this.GetType().FullName);
            }
    
            public void Query()
            {
                //Console.WriteLine("这里是{0}的Query", this.GetType().FullName);
            }
    
        }
    

    当我们使用普通方法是调用方法时。

    static void Main(string[] args)
            {
              
               
                    #region Common
                    DBHelper dbHelper = new DBHelper();
                    dbHelper.Query();
                    #endregion
             }
    

    首先我们了解下反射是怎么用的,

    1、动态加载Dll

    2、获取类型信息

    3、反射创建对象

                    #region 反射加载dll,读取module、类、方法、特性
                    Assembly assembly = Assembly.Load("DB.Sqlserver");//1 动态加载     默认加载当前路径的dll文件,不需要后缀
                    Assembly assembly1 = Assembly.LoadFile(@"D:\ASP.NetAdvanced\20170118Advanced8Course2Reflection\20170118Advanced8Course2Reflection\MyReflection7\MyReflection\bin\Debug\DB.Interface.dll");// 必须是完整路径
                    Assembly assembly2 = Assembly.LoadFrom("DB.Sqlserver.dll");// 可以是当前路径  也可以是完整路径
    
                    Console.WriteLine("************GetModules**********");
                    foreach (var item in assembly.GetModules())//获取模块信息
                    {
                        Console.WriteLine(item.FullyQualifiedName);
                    }
                    foreach (var item in assembly.GetTypes())//获取类型
                    {
                        Console.WriteLine(item.FullName);
                    }
                    Type typeDBHelper = assembly.GetType("DB.Sqlserver.DBHelper");//2 获取类型 (获取类型信息的方式不止一个)
                    foreach (var item in typeDBHelper.GetConstructors())//构造方法
                    {
                        Console.WriteLine(item.Name);
                    }
                    foreach (var item in typeDBHelper.GetProperties())//属性
                    {
                        Console.WriteLine(item.Name);
                    }
                    foreach (var item in typeDBHelper.GetMethods())//方法
                    {
                        Console.WriteLine(item.Name);
                    }
                    foreach (var item in typeDBHelper.GetFields())//字段
                    {
                        Console.WriteLine(item.Name);
                    }
    
                    #endregion
    

     下面有两种创建对象:一种直接创建,一种通过简单工厂+配置文件创建

    一:

                        object oDBHelper = Activator.CreateInstance(typeDBHelper);//3 创建对象
                        IDBHelper dbHelperReflection = (IDBHelper)oDBHelper;
                        dbHelperReflection.Query();
    

    二:

       public class SimpleFactor
        {
            private static string IDBHelperConfig = ConfigurationManager.AppSettings["IDBHelper"];
            private static string dllName = IDBHelperConfig.Split(',')[1];
            private static string className = IDBHelperConfig.Split(',')[0];
            public static IDBHelper CreateDBHelper()
            {
    
    
                Assembly assembly = Assembly.Load(dllName);
                Type type = assembly.GetType(className);
                object oObject = Activator.CreateInstance(type);
                return (IDBHelper)oObject;
    
            }
    }
    
                        IDBHelper dbHelperFactory = SimpleFactory.CreateDBHelper();
                        dbHelperFactory.Query();
    

    接下来就细细说下如何具体的使用泛型。

    首先我们创建一个反射测试类ReflectionTest:包含一些字段和重载方法。

    /// <summary>
        /// 反射测试类
        /// </summary>
        public class ReflectionTest
        {
            public int Id { get; set; }
            public string Name { get; set; }
    
            public string Field = null;
            public static string FieldStatic = null;
    
    
            #region 构造函数
            public ReflectionTest()
            {
                Console.WriteLine("这里是{0}无参数构造函数", this.GetType());
            }
    
            public ReflectionTest(string name)
            {
                Console.WriteLine("这里是{0} 有参数构造函数", this.GetType());
            }
    
            public ReflectionTest(int id, string name)
            {
                Console.WriteLine("这里是{0} 有参数构造函数", this.GetType());
            }
            #endregion
    
            public static void ShowStatic(string name)
            {
                Console.WriteLine("这里是{0}的ShowStatic", typeof(ReflectionTest));
            }
    
            public void ShowGeneric<T>(string name)
            {
                Console.WriteLine("这里是{0}的ShowStatic  T={1}", this.GetType(), typeof(T));
            }
    
            public void Show1()
            {
                Console.WriteLine("这里是{0}的Show1", this.GetType());
            }
    
            public void Show2(int id)
            {
    
                Console.WriteLine("这里是{0}的Show2", this.GetType());
            }
    
            public void Show3()
            {
                Console.WriteLine("这里是{0}的Show3_1", this.GetType());
            }
    
            public void Show3(int id, string name)
            {
                Console.WriteLine("这里是{0}的Show3", this.GetType());
            }
    
            public void Show3(string name, int id)
            {
                Console.WriteLine("这里是{0}的Show3_2", this.GetType());
            }
    
            public void Show3(int id)
            {
    
                Console.WriteLine("这里是{0}的Show3_3", this.GetType());
            }
    
            public void Show3(string name)
            {
    
                Console.WriteLine("这里是{0}的Show3_4", this.GetType());
            }
    
            private void Show4(string name)
            {
                Console.WriteLine("这里是{0}的Show4", this.GetType());
            }
    
        }
    

    我们如何通过反射来获取类的构造方法和泛型方法呢?

    还是跟上文的反射三大步骤一样,加载、获取类型、创建对象。

                Type typeTest = assembly.GetType("DB.Sqlserver.ReflectionTest");//2 获取类型 (获取类型信息的方式不止一个)
                        foreach (var item in typeTest.GetConstructors())
                        {
                            Console.WriteLine(item.Name);
                        }
                        Activator.CreateInstance(typeTest);//默认创建无参数构造函数
                        Activator.CreateInstance(typeTest, "demon");//带有string参数类型构造函数
                        Activator.CreateInstance(typeTest, 11, "限量版(397-限量版)");
                   
                       //获取泛型方法,泛型方法和普通方法不同的是,获取类型时要输入全称,而泛型方法是会声明占位符,所以输入全称时一定要加占位符有几个,占位符:`
                        Type typeGeneric = assembly.GetType("Ruanmou.DB.Sqlserver.GenericClass`1");
    //声明泛型类型 typeGeneric = typeGeneric.MakeGenericType(typeof(int)); Activator.CreateInstance(typeGeneric);

    反射调用实例方法、静态方法、重载方法 选修:调用私有方法 调用泛型方法。

         #region 反射调用实例方法、静态方法、重载方法 选修:调用私有方法 调用泛型方法
                    {
                        Console.WriteLine("**************反射调用实例方法****************");
                        Type typeTest = assembly.GetType("DB.Sqlserver.ReflectionTest");//2 获取类型 (获取类型信息的方式不止一个)
                        object oTest = Activator.CreateInstance(typeTest);
                        
    
                        foreach (var item in typeTest.GetMethods())
                        {
                            Console.WriteLine(item.Name);
                        }
                        {
                            MethodInfo method = typeTest.GetMethod("Show1");
                            method.Invoke(oTest, null);//调用show1无参数方法
                        }
                        {
                            MethodInfo method = typeTest.GetMethod("Show2");
                            method.Invoke(oTest, new object[] { 11 });//调用show2int参数方法
                        }
                        {
                            MethodInfo method = typeTest.GetMethod("ShowStatic");
                            method.Invoke(null, new object[] { "KOBE→Bryant" });//调用showstatic静态方法
                        }
                        {
                            MethodInfo method = typeTest.GetMethod("Show3", new Type[] { });
                            method.Invoke(oTest, null);
                        }
                        {
                            MethodInfo method = typeTest.GetMethod("Show3", new Type[] { typeof(int) });
                            method.Invoke(oTest, new object[] { 11 });
                        }
                        {
                            MethodInfo method = typeTest.GetMethod("Show3", new Type[] { typeof(string) });
                            method.Invoke(oTest, new object[] { "限量版(397-限量版)" });
                        }
                        {
                            MethodInfo method = typeTest.GetMethod("Show3", new Type[] { typeof(string), typeof(int) });
                            method.Invoke(oTest, new object[] { "书呆熊@拜仁", 22 });
                        }
                        {
                            MethodInfo method = typeTest.GetMethod("Show3", new Type[] { typeof(int), typeof(string) });
                            method.Invoke(oTest, new object[] { 33, "不懂微软" });
                        }
                        {
                            MethodInfo method = typeTest.GetMethod("Show4", BindingFlags.Instance | BindingFlags.NonPublic);//获取show4静态方法
                            method.Invoke(oTest, new object[] { "有木有" });
                        }
                        {
                            MethodInfo method = typeTest.GetMethod("ShowGeneric");
                            method = method.MakeGenericMethod(typeof(string));//声明泛型方法参数类型
                            method.Invoke(oTest, new object[] { "有木有" });
                        }
                    }
                    #endregion
    

    那么如何通过反射字段和属性,分别获取值和设置值

          #region 反射字段和属性,分别获取值和设置值
                    {
                        Console.WriteLine("**************反射字段和属性****************");
                      //普通形式的设置
                        ReflectionTest test = new ReflectionTest();
                        test.Id = 11;
                        test.Name = "为";
                        //泛型形式设置
                        Type typeTest = assembly.GetType("DB.Sqlserver.ReflectionTest");
                        object oTest = Activator.CreateInstance(typeTest);
                        foreach (var prop in typeTest.GetProperties())
                        {
                            Console.WriteLine(prop.GetValue(oTest));
                            Console.WriteLine(prop.Name);
                            if (prop.Name.Equals("Id"))
                            {
                                prop.SetValue(oTest, 22);
                            }
                            else if (prop.Name.Equals("Name"))
                            {
                                prop.SetValue(oTest, "ming");
                            }
    
                            Console.WriteLine(prop.GetValue(oTest));
                        }
                    }
    
    
                    #endregion 

    也许大家觉得这样设置字段还不如直接声明对象设置,这样通过反射设置字段的最主要目的是达到一个动态设置的目的。

    举个例子; 假设数据库中有张用户表,我们需要把用户表中的字段动态的添加到字段中,这个时候就是需要通过反射来实现这个功能。

    下面就简单的实现这个功能的部分代码

       using (SqlConnection conn = new SqlConnection(connString))
                {
                    conn.Open();
                    using (SqlCommand comm = new SqlCommand(sql.ToString(), conn))
                    {
                        using (SqlDataReader read = comm.ExecuteReader())
                        {
                            while (read.Read())
                            {
                                T model = new T();
                                foreach (PropertyInfo item in propertyInfos)
                                {
                                    if (!(read[item.Name] is DB))
                                        item.SetValue(model, read[item.Name]);//关键
                                }
                                results.Add(model);
                            }
                        }
                    }
                }
    

    说了这么多,那么泛型究竟有哪些好处和缺点呢。

    其实说白一点泛型就好比X射线,我们肉眼可以看到的方法都可以通过反射来调用。反射最主要的功能就是实现了方法的动态,达到了程序可拓展的目的。

    那么缺点呢,缺点吗,就是出现错误不易察觉。当我们通过new一个对象时,如果new的对象不对,编译器马上就会报错,而反射就是越过了编译器,比如动态加载DllAssembly assembly = Assembly.Load("DB.Sqlserver");//1 动态加载 

    如果你dll名称都写错了,编译器是不会察觉的,那后面错误了都不知道在哪。

    有人可能会担心泛型的一个效率的问题,下面做了一个简单的测试代码。

    #region
                    {
                        Stopwatch watch = new Stopwatch();
                        watch.Start();
                        for (int i = 0; i < 1000000; i++)
                        {
                            DBHelper dbHelper = new DBHelper();
                            dbHelper.Id = 1;
                            dbHelper.Name = "yy";
                            dbHelper.Query();
                        }
                        watch.Stop();
                        Console.WriteLine("普通方式花费{0}ms", watch.ElapsedMilliseconds);
                    }
                    {
                  Assembly assemblyTest = Assembly.Load("DB.Sqlserver");
    
                            Type typeTest = assemblyTest.GetType("DB.Sqlserver.DBHelper");
    
                        Stopwatch watch = new Stopwatch();
                        watch.Start();
                        for (int i = 0; i < 1000000; i++)
                        {
                           
                            object oTest = Activator.CreateInstance(typeTest);
    
                            foreach (var prop in typeTest.GetProperties())
                            {
                                if (prop.Name.Equals("Id"))
                                {
                                    prop.SetValue(oTest, 1);
                                }
                                else if (prop.Name.Equals("Name"))
                                {
                                    prop.SetValue(oTest, "yy");
                                }
                            }
                            MethodInfo method = typeTest.GetMethod("Query");
                            method.Invoke(oTest, null);
                        }
                        watch.Stop();
                        Console.WriteLine("反射方式花费{0}ms", watch.ElapsedMilliseconds);
                    }
                    #endregion
    

      

    方法重复一百万次反射需要的时间2秒不到,而且它们的绝对比误差会更低,所以说反射效率是不成问题的,除非你的程序效率已经精确到毫秒那就另当别论了。

  • 相关阅读:
    微信公众号支付java版本
    js数组去重
    原生js关闭窗口
    javascript常用代码片段
    kubeadm 生成的token过期后,集群增加节点
    Git 配置命令设置
    SpringBoot cookie工具类
    Docker中的Cgroup Driver:Cgroupfs 与 Systemd
    使用kubeadm 安装 kubernetes 1.12.0
    IPVS负载均衡
  • 原文地址:https://www.cnblogs.com/xima/p/7087602.html
Copyright © 2011-2022 走看看