C#反射基础知识_第1页
C#反射基础知识_第2页
C#反射基础知识_第3页
C#反射基础知识_第4页
C#反射基础知识_第5页
已阅读5页,还剩20页未读 继续免费阅读

付费下载

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、本文中部分内容参考自:1. Pro C# with .NET 3.0 Special Edition2. /JimmyZhang/archive/2008/01/27/Reflection-Part1.html一、基本概念反射 :反射是一个运行库类型发现的过程。 通过反射可以得到一个给定程序集所包含的所 有类型的列表, 这个列表包括给定类型中定义的方法、字段、 属性和事件。也可以动态的发 现一组给定类支持的借口、方法的参数和其他相关信息如基类、命名空间、数据清单等。、命名空间1.System.Reflection命名空间内的各类型(1 )Assembl

2、y通过它可以加载、了解和操纵一个程序集(2) AssemblyName 通过它可以找到大量隐藏在程序集的身份中的信息,如 版本信息、区域信息等(3) EventInfo 事件的信息(4) FieldInfo(5) MethodInfo字段的信息方法的信息(6) ParameterInfo 参数的信息(7 ) PropertyInfo 属性的信息(8) MemberInfo是抽象基类 ,为 EventInfo 、 FieldInfo 、 MethodInfoPropertyInfo 等类型定义了公共的行为。(9) Module用来访问带有多文件程序集的给定模块2.System.Type 类Sys

3、tem.Type 支持的成员可以分为这样几类(1) Is* 用来检查一个类型的元数据,如 IsAbstract 、 IsClass 、 IsValu eType 等等(2) Get* 用来从类型得到指定项目,如 GetEvent() 得到类型的一个指定 的事件( EventInfo )。 另外,这些方法都有一个单数版本和一个复数版本。如 GetEven t() 对应有一个复数版 本 GetEvents() ,该方法返回一个相关的 EventInfo 数组(3) FindMembers()根据查询条件 返回一个 MemberInfo 类型的数组(4)GetType() 该静态方法根据一个字符串名

4、称返回一个 Type 实例 (5)InvokeMember()对给定项目进行晚期绑定3. 得到一个 Type 类型实例的三种方法 ( 因为 Type 是一个抽象类,所以不能直接使用 new 关键字创建一个 Type 对象 )(1) 使用 System.Object.GetType()e.g: Pers onpe=newPers on();/ 定义 pe为person 类的一个对象Typet=pe.GetType();(2) 使用System.Type.GetType()静态方法,参数为类型的完全限定名e.g: Typet=Type.GetType(E ntity.Pers on ”);该方法被

5、重载,允许指定两个布尔类型的参数,一个用来控制当前类型不能找到时是否抛出异常,另一个用来指示是否区分字符串大小写e.g:Type t=Type.GetType(E ntity.P ers on ,false,true);注意到传入的字符串并没有包含类型所在的程序集信息,此时该类型便被认为是定义在当前执行的程序集中的。要得到一个外部私有程序集的类型元数据时,字符串参数必须使 用类型完全限定名加上类型所在程序集的友好名字e.g: Typet=Type.GetType(Entity.Person,Entity);/-En tity即为类型所在程序集的友好名字嵌套类型:传入的字符串可以指定一个+标记来

6、表示一个嵌套类型,如希望得到一个嵌套在person类中的枚举类型 City的类型信息,则可以这样 e.g:Type t=Type.GetType(E ntity.perso n+City);(3)使用 typeof 运算符 e.g:Type t=typeof(person);三种方法的比较:使用第一种方法必须先建立一个实例,而后两种方法不必 先建立实例。但使用typeof运算符仍然需要知道类型的编译时信息,而使用 System.Type.GetType()静态方法不需要知道类型的编译时信息,所以是首选方法。? ? ? ? ? ? ? ? ?下面是一个实例,简单的运用了前面介绍的知识,实现 了对

7、一个Type对象的反射,包括反射其所有可见字段、方法、属性、事件。反射类型的基 本属性。并将其中一个方法的详细信息列了出来【源代码】1 _us ing System;2 using System.Collecti on s.Ge neric;3 usingSystem.L inq;4 using System.Text;5 using System.Reflecti on;66 using System.Collections;/ 要实现IEnumerable接口则必须制定该命名空间897 n amespace Exercise8 LJLI12丨 public class BasePerson

8、 / 假设一个基类,定义了一个公共方法和一个私有方法1314publicvoid BasePublic()15时161718 1privatevoid BasePrivate()1920 121 122卜;23 12425/Person类实现了接口 IEn umerable,使得类中定义的Array 数组能够使用foreach 枚举26 1publicclassPers on :BasePers on, IEn umerable2728privatestringname =林田惠;/- 姓名29 1publicint age= 20;/- 年龄3031Array childre n=null;

9、 /-子女数组3233 1Pers on()3435 11 13637Pers on(stri nga, int b)3839 1Name = a;40 1Age = b;41 142 143 144 1publicstri ngName45 :46get returnname; 47 :set 48 149 150publicint Age5152get returnage; 53set 54 155 156 1publicvoid AddAge() /-自增一岁的方法57 口58Age+=1;59 160 16162一publicdelegatevoid Pers onN ameHa nd

10、ler(stri ngx);63 1publiceve nt Pers onN ameHa ndler On Cha ngeName;/了一个改变姓名的事件6465publicvoid ChangeName( stringnam) /-改名的方法66 口67Name = n am;6869一70 1publicvoidChan geNameA ndAddAge(str ingn ame,int age)具有两个参数的方法,用来演示反射具体方法的详细情况7172this.Name = n ame;73this.Age += age;74定义/-7576 丨public IEn umerator

11、GetE numerator。/-实现接口77茁 78 Ireturnchildre n.GetE numerator。;79 0 12 38 8 8 884卜 T T 扁一 Tclass Program180L1811.publicstaticvoid Main( stri ng args)182口183184nCo nsole.WriteL in e(IIII);1851-丿;186Type t = Type.GetType(Exercise.Pers on89构建自定义元数据查看器);/Person 类的完全限定名为 Exercise.Pers on187 I98T90di di TI

12、-111ListOtherIn fo(t);/反射其他一些信息ListFields(t);/反射字段ListProperties(t);/反射属性Listln terFaces(t);/反射接口ListEven ts(t);/反射事件ListMethodDetail(t,Cha ngeNameA ndAddAge);/反射一个/反射方法特定方法的详细信息194 IListMethods(t);195195 I196 IConsole.ReadLine();197 198 I 200 L201202【实现效果】【总结】结合源代码和运行效果,总结如下1. Name属性在编译后成为了get_Name

13、()和set_Name()两个独立的方法2. On Cha ngeNam事件的注册什=)和取消注册(-=)分别成为了 add_ On Cha ngeName() 禾口 remove_ On Cha ngeName 方法3. 私有(private) 字段name没有被打印出来4. 基类的基类System.Object的成员GetType()和Equals。也被打印了出来, 基类的共有方法也被打印出来为了更好的控制显示我们所想要的信息,下面简单介绍一下Fin dMembers()方法。MemberInfo mi = t.FindMembers (/【FindMembers 】MemberTypes

14、.Method,/【说明查找的成员类型为Method】Bi ndi ngFlags.Public |Bindin gFlags.Static |Bi ndi ngFlags.No nPublic |/【位屏蔽】Bindin gFlags .In sta nee |Bi ndin gFlags.DeclaredO nly,Type.FilterName ,/【执行比较的委托】);将上例的ListMethods 方法改为public static void ListMethods(Type t)该类型的所有方法:”);Co nsole.WriteL in e(ndMemberI nfo mi =

15、t.Fi ndMembers(/MemberTypes.Method,/【FindMembers 】【说明查找的成员类型为MethoBi ndin gFlags.Public |Bindin gFlags.Static |Bi ndi ngFlags.No nPublic |/【位屏敝】Bindin gFlags .In sta nee |Bi ndi ngFlags.DeclaredO nly,Type.FilterName,/【执行比较的委托】n*n);foreach (Methodinfo m in mi)Con sole.WriteLi ne(t方法名:0, m.Name);Type.

16、FilterName 返回一个MemberFilter类型的委托,它说明按照方法名称进行过滤,最后一个参数“ *说明返回所有名称(如果使用“ Get* ”,则会返回所有以 Get开头的方法)法 方名名名名名名名名名名 所方方方方方方方方方方 的该现在的输岀如下:jet Jame setitjLName get_Afe set_Age AddAge add_OnChangeNne renauejOnChangeName ChangeNameChancre HameAndAddAge GetEnume r-ator可以看到,所有继承而来的方法都没有显示。通过以上实例可以看到System.Refle

17、ction 命名空间和System.Type类允许我们反射 Type实例的大量信息。然而,对于当前创建的这个实例有一个很大的限制一一仅仅能访问当前的程序集,所以接下来要讨论:如何能使应用程序加载并反射在运行时并不知道的程序集?1. 动态加载程序集即使那些程序集没有在很多时候我们需要在运行时以编程的方式动态载入程序集,记录在程序清单中。按需加载外部程序集的操作被称为动态加载System.Reflection提供了一个名为 Assembly的类,使用它我们可以:(1) 动态加载程序集并找到关于程序集自身的属性(2) 动态加载私有或共享程序集(3) 加载任意位置的程序集从本质上说,Assembly类

18、提供的方法(尤其是Load ()和LoadFrom()使得我们可以用编程的方式提供和客户端.config文件中同样的信息下面通过一个实例来演示如何通过一个程序集的友好名称来动态加载一个程序集,并打印出其所包含的每个类、接口、委托等等信息。【源代码】1 us ingSystem;us ingSystem.Collectio ns.Ge neric;us ingSystem.L inq;us ingSystem.Text;uus ingSystem.Reflecti on;_n amespace ActiveLoad叫IJclass ProgramM T|/定义用来打印程序集中详细情况的函数Tst

19、aticvoidListAllTypes(Assembly asm)鹽1Co nsole.WriteL in e(程序集名称:0, asm.FullName);1Type types = asm.GetTypes();11foreach(Type tin types)黑1Con sole.WriteLi ne(n 类型:0t名称:1, t, t.Name);static void Main(stri ng args)Assembly asm = AssembIy.Load(Exercise ); /程序集的友好名称动态加载程序集,ActiveLoad项目必须添加对Exercise-根据项目的引

20、用/ListAIITypes(asm);asm = AssembIy.Load(ListAIITypes(asm);Con soIe.ReadL in e();LINDERMAN.E ntity);【运行效果】-曲 file;/D; f容整理ercise/ActiveLoad/bin/Debug/Act ive*.Uepsion.8.0.0, Culture neutral, PUblicKeToken =null名称:BasePe pson芙型 1 Exerc ise . EasePerson湊型:Exerc ise -Person 名称;Pers n型:Exerc ise . Person

21、-PeisonNanieHandleJ* 名称匕 Pera on Name Handlert Exercise名称* PiosriAfu注意:1. ActiveLoad 项目必须添加对 Exercise 项目的引用2.静态方法Assembly.Load方法仅仅传入了一个要加载到内存的程序集的友好名称,因此如果想反射Exercise.dll ,则需要把 Exercise.dll文件复制到 ActiveLoad 应用程序的Bi nDebug 目录,用:添加引用 - 浏览 - ActiveLoad 项目的 bin 文件夹 . 添加】3. 如果希望让 ActiveLoad 更加灵活, 可以使用 Ass

22、embly.LoadFrom 方法。此时 只要在想查看的程序集前面加上一个绝对路径。2. 晚期绑定 晚期绑定是一种创建一个给定类型的实例并在运行时调用其成员而不需要在编译时知道它存在 的一种技术,对于程序的可扩展性来说非常重要。要介绍晚期绑定技术,首先必须介绍一下 System.Activator 类。System.Activator 类除了继承自 Object 的方法外,其本身只定义了几个成员 方法,而且其中大多数都与 .net 远程处理有关为了建立一个晚期绑定类型的实例,当前我们只需关注 Activator.CreateI nstance() 方法。CreateInstance() 方法经

23、历过多次重载, 其中最简单的变化是带有一个有效 的 Type 对象,描述希望动态分配的实体。该方法将返回一个基本的 Object 类型而不是一个强类型并不是我们所 传入的类型。在 Exercise.person 类内加入打印个人信息的函数public void DisplayInfo()Console .WriteLine( 姓名: 0, 年龄: 1 ,this .Name, this .Age);下面新建一个 LataBinding 项目,通过晚期绑定来建立一个 Exercise.perso n 类的实例,并调用 DisplayInfo()【源代码】class Programstatic v

24、oid Main( string args)Assembly asm = Assembly .Load( Exercise);Type man = asm.GetType( Exercise.pers on );object Man = Activator .Create In sta nce(ma n);/- 返回一个 object 类型而不是 Exercise.person类型得到的实例 Man是一个 Object类型而不是一个 Exercise.person 类 型。且不能通过显示转换来解决问题,因为程序并不知道Exercise.person是什么注意:晚期绑定的重点是建立编译时未知的对

25、象的实例F面通过反射来实现打印属性的工作首先,使用Type.GetMethod方法修改源代码如下:.j_classProgramUU 山staticvoid Mai n( stri ngargs)TAssembly asm = Assembly.Load(Exercise);1Type man = asm.GetType(Exercise.Pers on);/、八.、八 注意大小写II1 1_1object Man = Activator.CreateI nsta nce(ma n);/返回一个 object类型而不是 Exercise.person类型Methodi nfo mi = man

26、 .GetMethod(Displayl nfo);null );mi.l nvoke(Ma n.Con sole.ReadL in e();其中Invoke方法含有两个参数,第一为Object类型,表示调用方法所依赖的实例对象;第二个参数为数组类型,表示调用方法或构造函数所使用的参数,当赋 值为NULL时表示调用无参方法。运行效果F面再演示一个调用有参数方法的实例:调用 Exercise.person的 ChangeNameAndAddAg方法using System;usingSystem.Collecti on s.Ge neric;using System.L inq;using Sy

27、stem.Text;using System.Reflecti on;n amespace LateB inding 日日I class ProgramIstatic void Main( stri ng args)IAssembly asm = Assembly.Load(Exercise );Type man = asm.GetType(Exercise.Person); / 注意大小写Iobject Man = Activator.CreateI nsta nce(ma n);/ 返回一个object类型而不是 Exercise.person类型DisplayI nfo );Method

28、Info mi = man.GetMethod(mi.I nvoke(Ma n,null );mi = man .GetMethod(Cha ngeNameA ndAddAge);object par =new object 2;par0=林田惠改名了 ”par1= 10;mi.ln voke(Ma n, par);/ 执行了改名并增长岁数的方法mi = man .GetMethod(mi.ln voke(Ma n.Displa ylnfo );null ); /改名后再显示个人信息Con sole.ReadL in e();【运行效果】|盂客整理风心-反射匯玄林田惠改名了年龄 303. 特性

29、编程特性就是用于类型(比如类、接口、结构)、成员(比如属性、方法)、程序集或模块的 代码注解。 NET程序员可以使用特性把更多的元数据嵌入到程序集中,用来修饰类型的行为。当在代码中应用特性时,如果它们没有被另一个软件显式的反射,那么嵌入的元数据基本没什么作用。反之,嵌入程序集中的元数据介绍将被忽略不计,而并无害处。就要在编译周期中寻找各种特性是否存在。2.射指定的特性除了开发工具,在.NET基类库中的许多方法也被设定为要反3.NET CLR也巡查某些特性是否存在4.用程序。用户可以构建反射自定义的特性和.NET基类库中的特性的应C#预定义特性的简单介绍CLSCompliant 强制被注解项遵从

30、 CLSDllImport允许.NET代码调用任意非托管的 C或C+基类库,包括操作系统中的 APIObsolete标记一个不用的类或成员Serializable标记一个类或结构可以被“序列化”NonSerialized指定类或结构中的某个字段不能在序列化过程中被持久化WebMethod 法的返回值序列化为标记一个方法可以通过 HTTP请求调用,并且通知CLR将方XML注意:1. 一个特性只能被应用在紧接下来的对象,例如 Serializable public class Person NonSerialized public float salary;public bool sex;publ

31、ic string hobbit;在该类中不能被序列化的仅是 salary ,而由于实体类中注释有 Serializable ,所以其他字段都可以被序列化2. 一个项可以被加上多种特性,如下: Serializable ,Obsolete ( 这个类已经过时了,请用更新的版本)public class Personpublic float salary;或者这样 Serializable Obsolete ( 这个类已经过时了,请用更新的版本 )public class Personpublic float salary;3. 可以为特性指定构造参数当给特性提供构造参数时, 直到该特性被其他类

32、型或外部工具反射后, 特性才 被分配大内存中, 定义在特性级的字符串数据只是作为元数据介绍被存储在程序集 中。也就是说,直到被其他代理反射,特性才发挥使用。4.C#特性的简化符号当名称转换时, 所有 .NET 特性,包括自己建立的自定义特性都将加上一个Attribute得后缀,但是为简化使用过程,C#语言不需要输入 Attribute 后缀。也即, Serializable Attribute public class Person和 Serializablepublic class Person效果是一样的。自定义特性(1)构建自定义特性构建自定义特性的第一步是建立一个新的派生自 Syste

33、m.Attribute 的 类,考虑到安全原因,把所有自定义特性都设计成密封的是一个好习惯为 Person 类设计一个特性类如下public sealed class personAttribute : System. Attributeprivate string _PersonData; /- 定义一个私有字段public personAttribute()public personAttribute( string Data) /- 带参数构造函数public String PersonData /- 维护私有字段的属性get return _PersonData; set _Perso

34、nData = value ; (2)使用自定义特性使用自定义特性有两种方法1. 位置参数 :使用自定义特性类的构造函数来为其所维护的私有字段 赋值如: Exercise.Program . personAttribute ( 这是在使用位置参数通过构造函数赋值 )public class Person : BasePerson , IEnumerableprivate string name = 林田惠 ; /- 姓名public int age=20;/- 年龄2. 命名参数 :使用自定义特性的属性成员来为其所维护的私有字段赋值如: Exercise. Program . personAt

35、tribute (PersonData= 这是在使用位置参 数通过构造函数赋值 )public class Person : BasePerson , IEnumerableprivate string name = 林田惠 ; /- 姓名public int age=20;/- 年龄限制特性的使用默认情况下自定义特性可以被应用在代码中几乎所有的方面 ( 方法、类、属性等 ) 。 有时希望限定自定义特性的应用范围,则需要在自定义特性的定义中应用 AttributeUsage 特性 ,AttributeUsage 特性支持任意 AttributeTargets 枚举值的组合例如,我们希望将刚刚定

36、义的特性变成只可应用于 类或结构,不能重复应用于一个项,且不能被应用于继承项,则可 如下修改定义: AttributeUsage ( AttributeTargets .Class| Attribute Targets .Struct,AllowMultiple= false ,Inherited= false ) public sealed class personAttribute : System. Attribute private string _PersonData; /- 定义一个私有字段public personAttribute()实例运用实例一 使用早期绑定反射特性 如果希

37、望使用早期绑定,则相应的特性需要客户应用程序在编译时定义 在 Exercise 命名空间下定义一个属性,将其设为可重复应用于同一个项上,代码如下 namespace Exercise AttributeUsage ( AttributeTargets .Class | AttributeTargets .Struct, AllowMultiple = true, Inherited = false)private string _PersonData;/- 定义一个私有字段public personDataAttribute()public personDataAttribute(string

38、 Data)/- 带参数构造函数_PersonData = Data;public String PersonData /- 维护私有字段的属性get return _PersonData; set _PersonData = value; 在 Person 类的定义上应用三个 personDataAttribute 属性如下: personData ( 这是第一个 personData 特性 ) personData ( 这是第二个 personData 特性 ) personData (PersonData = 这是第三个 personData 特性 )public class Perso

39、n : BasePerson , IEnumerableprivate string name = 林田惠 ; /- 姓名Array children=null;/- 子女数组public Person()设置 Exercise 项目为启动项,在 Exercise 命名空间下,修改 Main 函数如下public static void Main(string args)/* 演示使用早期绑定反射特性 */Type t = typeof( Person );Object customAtts = t.GetCustomAttributes(false);/-false代表不搜索继承链forea

40、ch ( personDataAttribute pda in customAtts)Console .WriteLine( 应用了一个 personDataAttribute 特性, 其中 personDa ta=0 ,pda.PersonData);Console .ReadLine();运行效果如下:e:v f lie;/D:/母博客整理S/C#_Jfe5t/Exercise/Exercise/bin/Debu/EKercise* .p?=应用了一个个个peonDataA Ctr ibut e pe rsonDa taAt tribut e personDataftttpibute2zppersonDta lpersonBata 3 z|speronData几点说明:1.Type.GetCustmeAttributes()ype代表的成员上的所有特性,包含一个2.该例子中之所以能够使用Person类进行描述,是因

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论