C#基础知识梳理系列十五:反射教学内容.doc
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_05.gif)
《C#基础知识梳理系列十五:反射教学内容.doc》由会员分享,可在线阅读,更多相关《C#基础知识梳理系列十五:反射教学内容.doc(29页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、Good is good, but better carries it.精益求精,善益求善。C#基础知识梳理系列十五:反射-摘要反射,一个很有用且有意思的特性。当动态创建某个类型的实例或是调用方法或是访问对象成员时通常会用到它,它是基于程序集及元数据而工作的,所以这一章我们来讨论一下程序集、反射如何工作、如何动态创建类型及对象等相关知识,甚至可以动态创建程序集。第一节应用程序域与程序集通过本系列的前面章节,我们已经知道,Windows为每个进程分配独立的内存空间地址,各个进程之间不能直接相互访问。Windows对.NET的支持是以宿主和COM的形式实现的,基于.NET平台语言实现的代码文件使用
2、WindowsPE的文件格式,CLR其实就是COM,相当于一个虚拟机(当然这个虚拟机可以部署到任意支持它的系统环境中),在安装.NETFramework时,CLR的组件与其他COM一样在Windows系统中享有同等的待遇,当CLR启动初始化时会创建一个应用程序域,应用程序域是一组程序集的逻辑容器,它会随着进程的终止而被卸载销毁,CLR把程序代码所需要的程序集加载到当前(或指定的)应用程序域内。CLR可以以其初始化时创建的应用程序域为基础再创建其他的新应用程序域,两个应用程序域中的代码不能直接访问,当然可以通过“中介”进行数据传送。新的程序域创建完后CLR完全可以卸载它,以同步方式调用AppDo
3、main.Unload方法即可,调用此方法后,CLR会挂起当前进程中的所有线程,接着查找并中止运行在即将卸载的程序域内的线程,然后进行垃圾回收,最后主线程恢复运行。任何Windows程序都可以寄宿CLR,一台机上可以安装多个版本的CLR。Windows在启动一个托管的程序时会先启动MSCorEE.dll中的一个方法,该方法在内部根据一个托管的可执行文件信息来加载相应版本的CLR,CLR初始完成之后,将程序集加载到应用程序域,最后CLR检查程序集的CLR头信息找到Main方法并执行它。第二节加载程序集程序集是所有类型的集合,它还有一个重要的东西就是元数据。JIT就是利用程序集的TypeRef和A
4、ssemblyRef等元数据来确定所引用的程序集及类型,这些元数据包括名称、版本、语言文化和公钥标记等,JIT就是根据这些信息来加载一个程序集到应用程序域中。如果要自己加载一个程序集,可以调用类型Assembly的LoadXXX系列方法。(1)Load重载系列该方法会按照一定的顺序查找指定目录中的程序集:先去GAC中查找(如果是一个强命名程序集),如果找不到,则去应用程序的基目录、子目录查找。如果都没找到,则抛出异常。如下代码加载程序集MyAssemblyB:stringassemblyName=MyAssemblyB,Version=1.0.0.0,Culture=neutral,Publi
5、cKeyToken=null;Assemblyassembly=Assembly.Load(assemblyName);(2)LoadFrom重载系列加载指定程序集名称或路径的程序集,其在内部调用Load方法,并且还可以指定一个网络路径,如果指定网络路径,则先下载该程序集,再将其加载到程序域,如下代码:Assembly.LoadFrom((3)LoadFile重载系列从任意路径加载一个程序集,并且可以从不同路径加载相同名称的程序集。在一个项目中,可能程序集之间都有依赖关系,也可以将一个程序集作为资源数据嵌入到一个程序集中,在需要时再加载该程序集,这时通过注册ResolveAssembly事件来
6、加载这个程序集。如下;AppDomain.CurrentDomain.AssemblyResolve+=(sender,arg)=bytebuffer=null;using(Streamstream=Assembly.GetExecutingAssembly().GetManifestResourceStream(ConsoleApp.MyAssemblyA.dll)buffer=newbytestream.Length;stream.Read(buffer,0,buffer.Length);returnAssembly.Load(buffer);以上代码要求必须先将MyAssemblyA.d
7、ll文件以资源形式嵌入到ConsoleApp项目中。这样在运行ConsoleApp程序时,如果使用了MyAssemblyA中的类型且未找到MyAssemblyA.dll文件,则会进入上面的事件方法来加载程序集MyAssemblyA。如果只是想了解一个程序集的元数据分析其类型而不调用类型的成员,为了提高性能,可以调用这些方法:Assembly.ReflectionOnlyLoadFrom(StringassemblyFile)Assembly.ReflectionOnlyLoad(byterawAssembly)Assembly.ReflectionOnlyLoad(Stringassembly
8、Name)如果试图调用上面这三个方法加载的程序集中类型的代码,则CLR会抛出异常。第三节反射我们知道,在程序集(或模块)内有一个很重要的数据就是元数据,它们描述了类型定义表,字段定义表,方法表等,也就是说所有的类型及成员定义项都会在这里被清楚详细地记录下来。很明显,如果我们拿到了这些“描述信息”,当然就相当于已经明确知道了一个类型及其成员,进而就可以“构造”这个类型,通过反射就可以达到这样的目的。另人高兴的是我们不用分析那些元数据就可以方便地得到程序集内的类型成员,.NETFramework提供了一些与此相关的类定义在命名空间System.Reflection下。反射提供了封装程序集、模块和类
9、型的对象(Type类型)。反射机制运行在程序运行时动态发现类型及其成员。(1)查找程序集内所定义的类型在将某一程序集加载到应用程序域后,可以通过Assembly的GetExportedTypes方法来获取该程序集所有的公开类型,如下代码:privatevoidGetTypes()stringassemblyName=MyAssemblyB,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null;Assemblyassembly=Assembly.Load(assemblyName);Typetypes=assembly.GetExportedTy
10、pes();foreach(Typetintypes)Console.WriteLine(t.Name);(2)查找类型成员在命名空间System.Reflection中有一个抽象类型MemberInfo,它封装了与类型成员相关的通用属性,每一个类型成员都有一个对应的从MemberInfo派生而来的类型,并且内置了一些特殊的属性特征,如FieldInfo、MethodBase(ContructorInfo、MethodInfo)、PropertyInfo和EventInfo。可以通过调用类型Type对象的GetMembers方法获取该类型的所有成员或相应成员,如下代码(对上面的GetTypes
11、方法的修改)获取全部成员列表:Typetypes=assembly.GetExportedTypes();foreach(Typetintypes)Console.WriteLine(t.Name);MemberInfomembers=t.GetMembers();Type有一组GetXXX方法是获取对象成员的,以下列出部分方法:GetConstructor/GetConstructors/获取构造函数GetEvent/GetEvents/获取事件GetField/GetFields/获取字段GetMethod/GetMethods/获取方法GetProperty/GetProperties/
12、获取属性并且每个方法都可以接收一个枚举类型BindingFlags的参数指定控制绑定和由反射执行的成员和类型搜索方法的标志。有关BindingFlags枚举可参考MSDN文档如下代码获取AudiCar类型的Owner属性和Run()方法:privatevoidGetTypeMethod()stringassemblyName=MyAssemblyB,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null;Assemblyassembly=Assembly.Load(assemblyName);Typet=assembly.GetType(MyAs
13、semblyB.AudiCar);MethodInfomethod=t.GetMethod(Run);PropertyInfopro=t.GetProperty(Owner);(3)构造类型实例在拿到类型及成员信息之后,我们就可以构造类型的实例对象了。FCL提供了几个方法来构造一个类型的实例对象,有关这些方法详细内容,可参考MSDN文档:Activator.CreateInstance()/重载系列Activator.CreateInstanceFrom()/重载系列AppDomain.CurrentDomain.CreateInstance()/重载系列AppDomain.CurrentDo
14、main.CreateInstanceFrom()/重载系列如下构造AudiCar类型的实例:privatevoidTestCreateInstance()stringassemblyName=MyAssemblyB,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null;Assemblyassembly=Assembly.Load(assemblyName);Typet=assembly.GetType(MyAssemblyB.AudiCar);varobj=Activator.CreateInstance(t);Debug.Assert(ob
15、j!=null);看一下调试:另外,还可以调用类型的构造函数创建实例对象,如下:obj=t.InvokeMember(AudiCar,BindingFlags.CreateInstance,null,null,null);第四节通过反射访问对象成员如果仅仅得到类型的对象,好像意义并不大,我们更多的是要操作对象,比如访问属性,调用方法等,这一节我们来看一下如何访问成员。类型Type提供了一个访问目标类型成员的非常靠谱的方法InvokeMember,调用此方法时,它会在类型成员中找到目标成员(这通常指定成员名称,也可以指定搜索筛选条件BindingFlags,如果调用的目标成员是方法,还可以给方法
16、传递参数。),如果找到则调用目标方法,并返回目标访问返回的结果,如果未找到,则抛出异常,如果是在目标方法内部有异常,则InvokeMember会先捕获该异常,包装后再抛出新的异常TargetInvocationException。以下是InvokeMember方法的原型:publicobjectInvokeMember(stringname,BindingFlagsinvokeAttr,Binderbinder,objecttarget,objectargs);publicobjectInvokeMember(stringname,BindingFlagsinvokeAttr,Binderbi
17、nder,objecttarget,objectargs,CultureInfoculture);publicabstractobjectInvokeMember(stringname,BindingFlagsinvokeAttr,Binderbinder,objecttarget,objectargs,ParameterModifiermodifiers,CultureInfoculture,stringnamedParameters);name目标方法名称invokeAttr查找成员筛选器binder规定了匹配成员和实参的规则target要调用其成员的对象args传递给目标方法的参数在上一
18、节的最后我们展示了如何调用类型的构造函数来实例化一个对象,下面的代码演示了如何调用对象的方法,其中方法Turn接收一个Direction类型的参数:stringassemblyName=MyAssemblyB,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null;Assemblyassembly=Assembly.Load(assemblyName);Typet=assembly.GetType(MyAssemblyB.AudiCar);varobj=Activator.CreateInstance(t);t.InvokeMember(Turn
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C# 基础知识 梳理 系列 十五 反射 教学内容
![提示](https://www.taowenge.com/images/bang_tan.gif)
限制150内