反射
什么是反射?
反射是在程序运行时动态获取和加载程序集
Reflection,中文翻译为反射。 这是.Net中获取运行时类型信息的方式,.Net的应用程序由几个部分:‘程序集(Assembly)’、‘模块(Module)’、‘类型(class)’组成,而反射提供一种编程的方式,让程序员可以在程序运行期获得这几个组成部分的相关信息。
反射的主要类:
System.Type 类--通过这个类可以访问任何给定数据类型的信息。
System.Reflection.Assembly类--它可以用于访问给定程序集的信息,或者把这个程序集加载到程序中。
诸如此类还有:
(1)使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。
(2)使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。
(3)使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。
(4)使用FiedInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。
(5)使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。
(6)使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。
(7)使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。
具体详见:https://msdn.microsoft.com/zh-cn/library/f7ykdhsy.aspx
反射技术在很多应用程序中有着不可替代的作用。例如在应用程序部署完成后,在使用过程中,用户的需求发生了变化,要对程序的功能进行临时的扩充或修改,这时可以不修改源程序,使用反射技术获取程序集中的方法或成员,动态地创建对象,并调用其中的方法,实现程序功能的扩充
反射单独实现方法:
一、加载程序集
(1)load方法是极力推荐的一种方法,load方法带有一个程序集的标识并载入它
AssemblymyAbly=Assembly.Load("System.Drawing");
(2)LoadFrom 方法 传递一个程序集文件的路径名。(不推荐)
Assembly assembly = Assembly.LoadFrom(@”D:\Test\MyAssembly.dll”);
(3)LoadWithPartialName:不要用这个方法,因为应用程序不能确定再载入的程序集的版本
二、利用反射获取类型信息
获得类型信息有两种方法:
一种是根据已知实例对象获取类型信息,其方法是通过调用System.Object上声明的方法GetType来获取实例对象的类型;
另外一种是通过Type.GetType或Assembly.GetType方法获取类型信息,需要注意的是,要查找一个类,必须指定它所在的程序集(编译出来的dll或exe文件),或者在已经获得的Assembly实例上调用GetType。
三、创建对象
通过加载一个程序集和在该程序集中搜索类型,获取所需要的类型后,就可以使用System.Activator类的CreateInstance方法创建此类型的一个实例。CreateInstance方法允许指定想要创建的对象,并且可选择的参数会应用到该对象的构造器上
四、调用被反射
调用被反射对象中的方法,可以使用Type类的InvokeMember方法,对该方法的声明如下:
publicobjectInvokeMember(stringname,BindingFlagsinvokeAttr,Binderbinder,objecttarget,object[]args);
(第一个参数是要调用的方法名;第二个参数是绑定标志,用来通知反射要寻找的内容;第三个参数是绑定器,当不需要绑定器时,可以设为null;第四个参数是调用成员的实例;第五个参数是要传递的参数,如果无参数可以设置为null。)
反射与自身去实例化有何区别,用反射的意义何在?
引用
以前获得程序集都是在编译的时候,编译时候引用添加Dll,优点是方法比较快,节省了寻找的时间,缺点是不够灵活,编译的时候已经确定了关系,如果后期出现变化的化,需要停止运行程序,重新编译重新生成
反射
反射编译时没有任何引用,而是运行的时候把dll里面的类创建出来,通过反射机制将方法、类、属性、字段等都可以获取出来简单来说就是在程序运行时就把类中的方法加载出来,编译时候无任何依赖。但是反射的速度会比较慢,因为需要寻找dll的过程,而且反射跨越作用域的限制(如私有方法private),也会构成不安全因素,而反射的优势就是灵活
小结
引用new 是编写程序的时候你知道要加载那个类.反射 就是你当时不知道要加载什么类,而是在程序运行时自动去获取。
举例说明
(1)比如说插件技术,做插件,比如说,我有个文本编辑器,里面原来有的功能是改变字体大小就没别的功能了,但这个文本编辑器实现了支持插件的技术,也就是说里面可以用反射实现.
我要再添加一个功能就是改变字体颜色.做一个插件,放到指定的文件夹下,编辑器就会自动添加这个功能.
这个到底怎么实现呢? 如果你用using 引用程序就写死了. 怎么会写死了呢?比如说我的主程序就是个文本编辑器,我的原来的改变字体大小在另外的程序集,你就要引用
using 改变字体大小(假设这里的"改变字体大小是个程序集"),
但我要再加一个改变颜色的功能,他又是一个程序集,这样你就要改代码,变成
using 改变字体大小;
using 改变字体颜色;
要在程序里面写多 "using 改变字体颜色;" 这种代码;但如果你用反射,就可以动态加载,不用写每个的 using .比如说,把这个两个程序集放在指定文件夹下,你就可以在程序里面用反射去读取里面有什么程序集,然后实现里面的功能.反射的目的就是为了扩展未知的应用。
(2)比如一个大型的网站,你在添加新的功能时,如果用引用方法的话,你就需要让整个网站停下来,从新进行编译和发布。这样显然是不可行的,向淘宝这样的大网站,每日访问量都上亿,如果它停下一分钟,那么它的损失可想而知。所以用了反射机制,你只需要把新加的功能编译成Dll或exe文件,放到程序运行加载位置,在配置文件中加入此新的功能,只需要保存一下。那么在客户端运行时,只要用户需要这个功能,那么程序自然就会通过接口、反射、配置文件等机制找到相应的dll或exe,从而使程序得到正常的运行。
总结:一个程序,这个程序定义了一些接口,只要实现了这些接口的dll都可以作为插件来插入到这个程序中。那么怎么实现呢?就可以通过反射来实现。就是把dll加载进内存,然后通过反射的方式来调用dll中的方法。
很多工厂模式就是使用的反射。
现实中的应用
机房合作
从最简单的更换数据库,我们开始使用了反射的功能,在配置文件里,添加add key的标签,以表明现在需要是连接的哪个数据库,将数据库的链接字符串放在配置文件里。
在工厂层里,工厂层的主要作用是应用配置文件和反射技术实现数据库的更换功能或者应用类的更换功能。
在Factory层中首先定义程序集的名字和命名空间的名字程序会自动通过反射读取配置文件中的相应字符,按照路径实例化出相应的对象。
执行到Factory函数时,通过Assembly.load加载程序集,并实例化(CreateInstance)相应的类再通过向上转型转换成接口类Interface,然后通过调用接口类中的函数来调用D层中实现该接口的函数。
当我们需要更换数据库的时候只需要更改配置文件里的字符串,就可以更改数据库而不需要动其它层的内容。通过将使用的数据库的名字用配置文件中key值来代替便可以十分方便的更换数据库。。
下面讲更换类,大家做机房时,设计分层和每层中的类,都是根据数据库的表进行设计的,这样在编写工厂层和配置文件中,我们只需要把自己D层的所有类用配置文件中的标签中的key和value表示出来,这样在工厂层通过反射语句实例化相应的D层的类,当需要更换类或者表的时候,就可以直接通过配置文件进行更换。
IOC容器与反射的结合(ITOO里面)
WCF只依赖于B层接口,把B层的生成路径改为WCF下的bin,这样就可以通过反射机制把B层的类创建出来,放在IOC容器里面。所以jenkins在配置的时候只需要配置WCF的,B和D都不需要配置。
service服务端中webconfig中声明了好多id和类,这样在程序启动的时候,就已经创建好了这些类,后面的type说明他处于哪个程序集里面的那个类,通过这个就可以找到这个程序集
IOC容器在启动的时候在配置文件中放入所有的id和类,放在一个大的哈希表里面(一个key和一value)id值就相当于key值,后面的类就代表他的value值。
这个框架的好处就是利用容器管理这些类之间的关系,不好处就是太慢了,程序启动的时候用的不用的类都准备好了,第一次启动的时候慢,但是后面启动就比较快
serviceFactory是用来管理WCF的所有接口