.Net开发笔记(二十一) 反射在.net中的应用

反射概念在网上处处都有,可是讲到的具体的应用不多,一个重要的缘由是现实中真的不多用获得它。引用msdn上对“反射”的解释:html

"经过 System.Reflection 命名空间中的类以及 System.Type,您能够获取有关已加载的程序集和在其中定义的类型(如类、接口和值类型)的信息。 您也可使用反射在运行时建立类型实例,以及调用和访问这些实例。"编辑器

这个解释着实让人难以理解,至少对新手来说,一头雾水。那么这篇文章我首先从概念下手,用一种尽可能易于理解的方式解释一下反射究竟是个什么东西。文章最后附加一个“反射”应用的demo,它能监放任何一个程序集中的任何一个Control(深度优先顺序遍历全部子控件)的全部事件信息。ide

在程序开发阶段,若是咱们要使用一个类型(包括实例化该类型对象,访问对象等等操做),分三个步骤:性能

  • 添加包含了这个类型程序集的引用(.net自带的类型程序集默认已引用);
  • 代码中直接使用该类型(用一种文本字符的形式,好比类型名、方法名、属性名);
  • 编译正确经过,程序运行。

图1this

以上是咱们经常使用的开发步骤,几乎不用去想为何要这样,每一个人都会。这个流程中第一个前提就是“要引用包含了这个类型的程序集”。假设某一次开发过程当中,咱们不能提早引用到包含这个类型的程序集(先不要否认这种状况,只能说明你没碰到),那么咱们改怎么写代码?正常状况下,咱们访问A类是这样的(假设A类在A.dll程序集中):spa

1 A a=new A();
2 a.PropertyName=”123”;
3 a.EventName+=a_EventName;
4 a.DoSomething();
View Code

正常编译经过。可是如今咱们没有引用A.dll,咱们改怎么写代码?仍是像上面那样写吗?不对,由于编译通不过,编译器会提示“缺乏对程序集的引用”(这个很容易理解,由于你没有引用程序集,编译器确定不会知道)。.net

以上就是咱们会碰到的一种状况,即:插件

在有些时候,咱们可能会使用一种数据类型,可是开发阶段并不能引用到包含该类型的程序集,包含了这个类型的程序集只能在程序运行起来以后,动态的引用进来。开发阶段引用程序集若是叫“静态引用程序集”,那么运行时引用程序集就应该叫“动态引用程序集”了。后者会形成一个问题:咱们该怎么写代码去访问动态引用程序集中的类型3d

图2code

这个时候,反射的做用就出来了。反射可以让咱们使用在编译阶段编译器不知道的数据类型(注意是编译器不知道,不是咱们不知道)。再举前面使用A类型的例子,咱们在开发阶段没有引用A.dll程序集,所以下面的代码没法经过编译:

1 A a=new A();
2 a.PropertyName=”123”;
3 a.EventName+=a_EventName;
4 a.DoSomething();
View Code

 可是,咱们知道程序运行以后,能够动态引用到A.dll,那么如今代码中怎么使用A类型呢?看下面的代码:

 1 Assembly assembly=Assembly.LoadFile(“C:\\A.dll”); //动态引用A.dll
 2 Type t = assembly.GetType(“ReflectionTestNS.A”); //获取A类型在程序集中的信息
 3 object oj=Activator.CreateInstance(t); //相似new A()
 4            PropertyInfo p = t.GetProperty("PropertyName"); 
 5             if (p != null)
 6             {
 7                 p.SetValue(oj, “123”, null);  //相似oj.PropertyName=”123”
 8             }
 9             EventInfo ei = t.GetEvent("EventName"); 
10             if (ei != null)
11             {
12                 Type tt = ei.EventHandlerType;
13                 ei.AddEventHandler(oj, Delegate.CreateDelegate(tt, this, "oj_EventName")); 
14                 //相似oj.EventName+=oj_EventName
15 }
16             MethodInfo m = t.GetMethod("DoSomething", new Type[] { });
17             if (m != null)
18             {
19                 m.Invoke(oj, null); //相似oj.DoSomething()
20             }
View Code

上面代码可以经过编译,咱们能够经过以上代码去访问A.dll程序集中的A类型,即便在开发阶段咱们没有A.dll的引用。须要注意的几点有:

1)虽然咱们在开发阶段不能引用到A.dll程序集,可是咱们应该对A.dll中的类型有了解,知道命名空间,知道数据类型名称,知道方法名称参数类型,知道事件名称委托类型等等,也就是说,虽然编译器不知道A.dll中的类型信息,咱们开发人员必须知道A.dll中的类型信息,这样以来,咱们才能利用“反射”加上“文本字符”做为标示去访问这个类型。

2)1)中规定的开发人员必须了解A.dll中的类型信息,仅仅是当你须要详细的使用一个类型对象时,若是你只须要获取A.dll中的有哪些类型、每一个类型有哪些方法参数属性事件等,而后将他们的信息显示出来,彻底不必知道A.dll中的类型信息,好比VS中编辑器的智能提示功能,或者Reflector等利用反射实现数据集中类型信息显示的软件,它们的开发人员知道你的程序集信息吗?不知道,可是仍是能工做很好。可是就上面“使用A类型”的例子来说,你必须知道A.dll中的A类型中有个叫EventName的事件,你才能给它的对象注册事件,不然能够说你根本使用不了A类型的对象。

3)编译阶段,对象和方法就能够关联起来(好比a.DoSomething()能经过编译),这种若是称之为“早期绑定”(early binding),那么经过反射将对象和方法关联起来就称为“晚期绑定”(late binding)。前者在编译阶段编译器能够检查正确性,后者编译器无能为力,由于编译器不知道A.dll的任何信息。

一张图区分两种访问程序集中类型的区别:

图3

我的认为,正常开发中用不到反射,因此尽可能避免使用反射(反射有缺陷,运行性能编译阶段不能检查正确性等),本系列博客(十七)中讲到的扩展应用程序,就使用到了反射,文中指出将插件打包成dll程序集后,放入宿主程序的plugins目录中,宿主程序启动后,会动态引用plugins目录中的程序集,动态建立插件类型实例,而后访问它。那么若是你是宿主程序的开发人员,你会在开发阶段引用到第三方开发的插件程序集dll文件吗?不能,可是你仍是得在代码中使用它的类型。

注:上面扩展应用程序中不全使用反射去访问动态引用程序集中的类型,由于它使用到了一个IPlugin的接口,动态实例化插件对象后,是使用IPlugin接口引用这个对象,以后全部的都是经过这个接口去访问对象(以后没有使用到反射),它避免了使用反射的性能问题和在编译阶段可以检查程序的正确性(开发阶段宿主程序可以引用IPlugin接口程序集),这个也是必须使用反射场合的一种改进,后续有机会我会详细说明。

另外网上有不少讲述反射的文章,都是用相似以下代码做为反射应用实例,

 1 void btn1_Click(object sender,EventArgs e)
 2 {
 3 Type t = typeof(Button);
 4 //或者
 5 Type t = btn1.GetType();
 6 PropertyInfo p = t.GetProperty(“Text”);
 7 if(p!=null)
 8 {
 9     p.SetValue(btn1,”123”,null); //利用反射编辑btn1的Text属性
10 }
11 }
View Code

以上相似代码并无错误,只是我以为会给人误导,反射的真正使用场合不在这里(这里彻底用不着,为何不直接使用btn1.Text=”123”呢?),看多了,人们就会认为反射就是这做用,用在这里。

Demo中包含了两个项目,一个是简单的说明了正常方法使用BackgroundWorker这个类型,和动态引用程序集动态建立BackgroundWorker类型对象(伪装开发阶段没有引用包含BackgroundWorker类型的程序集),二者的区别。另外一个项目可以动态引用程序集,而且动态实例化Control类实例,关键还能监放任何控件的全部事件,而后输出事件信息,这个有点复杂,不只仅使用到了System.Reflection命名空间中的类型,还用了System.Reflection.Emit命名空间中的类型,后者能够动态建立类型,因为每一个控件的每一个事件类型不同,而且个数还不肯定,因此咱们没有办法事先定义一个通用的事件注册者,只能挨个为每一个事件动态建立一个事件注册者类。第二个项目流程见下图:

图4

第二个项目参见了CodeProject上老外的一篇文章(http://www.codeproject.com/Articles/3317/ControlInspector-monitor-Windows-Forms-events-as-t),注释请参见个人,代码中有详细的中文解释。

Demo截图:

图5 静态引用程序集访问类型 和 动态引用程序集访问类型的区别

图6 反射应用

总之,反射可以让你使用在编译阶段还不可达的程序集(类型)。

源码下载地址:http://files.cnblogs.com/xiaozhi_5638/ReflectionTest.rar

 

但愿有帮助!

相关文章
相关标签/搜索