博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
6.C#知识点:反射
阅读量:6279 次
发布时间:2019-06-22

本文共 8970 字,大约阅读时间需要 29 分钟。

1.反射是什么?

  反射提供描述组件,模块和类型的对象(类型为Type)。您可以使用反射来动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型,并调用其方法或访问其字段和属性。如果您在代码中使用属性,反射使您可以访问它们。有关更多信息,请参阅属性。-----来自微软官方。

  微软的解释我觉得还可以。用大白话讲就是我们可以以通过反射让我们知道位置类型的信息。类似显示生活中的B超啊。医生用B超看到孕妇肚子里的内部情况,因为医生无法从内部查看。反射也是一样,对于位置类型。或者引用过来的dll。我们是不知道内部情况的。但是可以通过反射。蝙蝠的超声波也是。通过声波反射回来,得知前方是否有障碍。这就是反射的功能。如果要问反射内部是如何实现的。不好意思。目前我也不知道。哈哈哈哈。

  简单的来说,我们的程序是有dll的组成的,dll里面有许许多多的类组成。类里面又有字段,属性和方法。反射的作用就是给个dll就能知道有哪些类,通过类又能知道有哪些成员。那么.net里面的反射是怎么做到呢?那下面就要介绍几个种类的反射类了。

    (1)使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。 

    (2)使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。 
    (3)使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。 
    (4)使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。
    (5)使用FiedInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。
    (6)使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。 
    (7)使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。 
    (8)使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。这段话是从大牛的博客拷贝------->

  

下面我依次重点的介绍几个详细的类。

首先是Assembly。这个存在于System.Reflection命名空间下面。我主要讲它的3个加载程序集的的方法和区别。Load,LoadForm,LoadFile。

讲了这门多文字,先从代码看看语法。

using System;using System.Collections.Generic;using System.Linq;using System.Reflection;using System.Text;using System.Threading.Tasks;namespace 反射Demo{    class Program    {        static void Main(string[] args)        {            //加载程序集            Assembly assembly = Assembly.Load("TestDLL");            //输出程序集的强名称            Console.WriteLine(assembly.FullName);            Console.ReadKey();        }    }}

Load方法就是通过程序集的的名称加载程序,但是需要要加载的程序集在当前程序集的bin目录下才能找得到。

LoadForm

using System;using System.Collections.Generic;using System.Linq;using System.Reflection;using System.Text;using System.Threading.Tasks;namespace 反射Demo{    class Program    {        static void Main(string[] args)        {            #region Load方法                    //加载程序集                   // Assembly assembly = Assembly.Load("TestDLL");                    //输出程序集的强名称                    //Console.WriteLine(assembly.FullName);                    //Console.ReadKey();            #endregion            Assembly assmbly1 = Assembly.LoadFrom(@"C:\Users\DH\Documents\visual studio 2017\Projects\反射Demo\TestDLL\bin\Debug\TestDLL.dll");            Console.WriteLine(assmbly1.FullName);            Console.ReadKey();        }    }}

LoadForm是通过路径进行创建。返回加载的程序集。

来看最后一个loadFile

using System;using System.Collections.Generic;using System.Linq;using System.Reflection;using System.Text;using System.Threading.Tasks;namespace 反射Demo{    class Program    {        static void Main(string[] args)        {            #region Load方法            //加载程序集            // Assembly assembly = Assembly.Load("TestDLL");            //输出程序集的强名称            //Console.WriteLine(assembly.FullName);            //Console.ReadKey();            #endregion            #region LoadFrom方法            //Assembly assmbly1 = Assembly.LoadFrom(@"C:\Users\DH\Documents\visual studio 2017\Projects\反射Demo\TestDLL\bin\Debug\TestDLL.dll");            //Console.WriteLine(assmbly1.FullName);            ///Console.ReadKey();            #endregion            #region LoadFile方法            Assembly assmbly2 = Assembly.LoadFile(@"C:\Users\DH\Documents\visual studio 2017\Projects\反射Demo\TestDLL\bin\Debug\TestDLL.dll");            Console.WriteLine(assmbly2.FullName);            Console.ReadKey();            #endregion        }    }}

这个三个方法语法都很简单。现在来说说这个三个的区别,和优缺点。

LoadFrom和Load:LoadForm同一个程序集只加载一次,就算加载了不通的路径相同的程序集,他也只能给你返回之前的程序集,LoadFrom只能用于加载不同标识的程序集, 也就是唯一的程序集, 不能用于加载标识相同但路径不同的程序集。 

LoadFile和LoadForm:loadFile只会加载本身程序集,不会加在其引用的程序集,LoadForm和Load是会加载的其引用程序集。而且LoadFile同一个程序集只能加载一次。这个和LoadForm是一样。

从性能上看 LoadForm没有Load好,功能上也是load强一点。应用的时候如果loadFom和load都满足需求,建议用load。

这几个方法还几个重载版本。由于本片只是基础篇。篇幅不宜过多。想深入了解的小伙伴可以查询MSN看看文档。最详细的说明就是文档。但是文档说的比较官方。结合大牛们写的博客。更容易懂一点。

好了我们开始下一个阶段了。程序集现在我们得了。我开始看看程序集里面有些啥?

 

using System;using System.Collections.Generic;using System.Linq;using System.Reflection;using System.Text;using System.Threading.Tasks;namespace 反射Demo{    class Program    {        static void Main(string[] args)        {            #region LoadFrom方法            Assembly assmbly = Assembly.LoadFrom(@"C:\Users\DH\Documents\visual studio 2017\Projects\反射Demo\TestDLL\bin\Debug\TestDLL.dll");            Type[] types = assmbly.GetTypes();            foreach (var item in types)            {                Console.WriteLine("类:"+item.Name);            }            Console.ReadKey();            #endregion        }    }}

assmbly.GetTypes()这个方法或获取了所有类。 下面我我展示下获取字段和方法的字段
using System;using System.Collections.Generic;using System.Linq;using System.Reflection;using System.Text;using System.Threading.Tasks;namespace 反射Demo{    class Program    {        static void Main(string[] args)        {            #region LoadFrom方法            Assembly assmbly = Assembly.LoadFrom(@"C:\Users\DH\Documents\visual studio 2017\Projects\反射Demo\TestDLL\bin\Debug\TestDLL.dll");            Type[] types = assmbly.GetTypes();            foreach (var classitem in types)            {                Console.WriteLine("类:"+ classitem.Name);                foreach (var fileditem in classitem.GetFields())                {                    Console.WriteLine("字段名:"+ fileditem.Name);                }                foreach (var methodItem in classitem.GetMethods())                {                    Console.WriteLine("方法名:"+methodItem.Name);                }            }            Console.ReadKey();            #endregion        }    }}

 

可以看的出来我们将字段和方法名都给反射出来了,但是有个问题就是我们父类的方法也给弄出来了。我们可以修改下。这个地方有个重载版本。GetMethods有个重载方法可以通过BindingFlags枚举参数进行筛选父类的方法,或者其他的。 BindingFlags这个枚举类型。这里就不多讲。未来我会慢慢补全。

简单的就是这么多了。反射能做好多事情,非常灵活。我们抽象工厂里面就会用到反射。我们的工厂。就是通过反射创建出来。下面我写个demo演示下其作用。

 

using System;using System.Collections.Generic;using System.Linq;using System.Reflection;using System.Text;using System.Threading.Tasks;using TestDLL;namespace 反射Demo{    class Program    {        static void Main(string[] args)        {            Assembly assmbly = Assembly.Load("TestDLL");            SqlServerHelper helper =(SqlServerHelper)assmbly.CreateInstance("TestDLL.SqlServerHelper");            helper.Query();            Console.ReadKey();        }    }}

小伙伴们可以发现。我们实例化了一个SqlServerHelper对象,但是我们没有用正常的new方法,而是用了反射。这个时候有的小伙伴可能就会说这是脱裤子放屁,直接new多简单。在这里是没错,但是放在真正的项目里直接new是当时爽,需求变动就等着哭吧,比如说以后领导对你说,公司的数据库不用SqlServer了,换成Oracle了。这是时候如果你是new的话还要改这里的代码,实际情况可能更复杂。但是用了我们的反射,这种烦恼就不会存在了。

CreateInstance("TestDLL.SqlServerHelper") 这个方法参数我们完全可以通过配置文件修改。这个类就不需要变了。我来演示下代码

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace TestDLL{    public interface IDBHelper    {        void Query();    }}

 先创建一个约定数据操作的接口 IDBHlper类。规定有一个Query方法,然后让SqlServerHelper继承这个接口,并且实现这个方法。

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace TestDLL{    public class SqlServerHelper: IDBHelper    {        public void Query()        {            Console.WriteLine("这是测试");        }    }}

 然后修改mian函数的方法

using System;using System.Collections.Generic;using System.Configuration;using System.Linq;using System.Reflection;using System.Text;using System.Threading.Tasks;using TestDLL;namespace 反射Demo{    class Program    {        static void Main(string[] args)        {            Assembly assmbly = Assembly.Load("TestDLL");            string helperkey = ConfigurationManager.AppSettings["Helper"];            IDBHelper helper =(IDBHelper)assmbly.CreateInstance(helperkey);            helper.Query();            Console.ReadKey();        }    }}

这时候如果要将mian函数里面helper切换成Oracle的只要添加一个继承于IDBheper接口的类,然后实现方法,在修改配置文件指向这个类,然后就可以了。对于main函数我们是一点不需要动的,这是就是我们所有的高内聚低耦合。完成解耦。易于修改。

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace TestDLL{    public class OracleHelper : IDBHelper    {        public void Query()        {            Console.WriteLine("我是Orcle数据库");        }    }}
using System;using System.Collections.Generic;using System.Configuration;using System.Linq;using System.Reflection;using System.Text;using System.Threading.Tasks;using TestDLL;namespace 反射Demo{    class Program    {        static void Main(string[] args)        {            Assembly assmbly = Assembly.Load("TestDLL");            string helperkey = ConfigurationManager.AppSettings["Helper"];            IDBHelper helper =(IDBHelper)assmbly.CreateInstance(helperkey);            helper.Query();            Console.ReadKey();        }    }}

mian函数一点都没有变化。运行查看结果

到了这个里,我已经演示了一个反射应用场景了。其实VS本身就很多地方用了反射。比如。创建对象调用方法时候VS直接只能帮我们列出这个对象下的成员。这个就是通过反射。其实还有很多。等待大家去发现。反射应该属于C#里面的高级知识点了。目前所说的只是冰山一角。

Ok。讲到这里就结束了哈。

如果刚开始学习的小伙伴还有疑问的话,可以评论咱们一起学习。

如果哪位大牛随便瞄到个错误,也请告之我,让我能够进步。

 

 

世上本来没有路,走的人多了,就是路了。世上也没有绝对的真理,你成功了,你就是真理。
 
分类: 

转载地址:http://pgyva.baihongyu.com/

你可能感兴趣的文章
引用 LPSTR、LPCSTR、LPTSTR、LPCTSTR、LPWSTR及LPCWSTR的意义及区别
查看>>
Codeforces 527C Glass Carving(Set)
查看>>
PHP获取表单方法
查看>>
SQLServer复制表
查看>>
乞讨 间隔[a,b]在见面p^k*q*^m(k>m)中数号码
查看>>
验证LeetCode Surrounded Regions 包围区域的DFS方法
查看>>
Mongodb 之insert瞬时完成,测试数据---飞天博客
查看>>
实时监听输入框值变化:oninput & onpropertychange
查看>>
消息队列规范及实现
查看>>
jQuery整理您的笔记----jQuery开始
查看>>
多数据源问题--Spring+Ibatis 访问多个数据源(非分布式事务)
查看>>
vim 设置 swap file, 防止 同一个文件同时被多次打开,而且有恢复的功效
查看>>
架构之路(三) 单元测试
查看>>
virtual memory exhausted: Cannot allocate memory
查看>>
Android百度地图开发02之添加覆盖物 + 地理编码和反地理编码
查看>>
nodejs显现events.js:72抛出错误
查看>>
MFC中获取命令行参数的几种方法
查看>>
HTTP协议详解(真的很经典)
查看>>
web_find和web_reg_find的用法和区别
查看>>
iOS:图像选取器控制器控件UIImagePickerController的详解
查看>>