C#讲义-17 特性和反射_第1页
C#讲义-17 特性和反射_第2页
C#讲义-17 特性和反射_第3页
C#讲义-17 特性和反射_第4页
C#讲义-17 特性和反射_第5页
已阅读5页,还剩32页未读 继续免费阅读

下载本文档

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

文档简介

117特性和反射特性内置特性信息自定义特性浏览元数据反射所有类型反射一个类型反射类型的成员MemberInfo迟绑定反射发送动态调用接口反射发送21特性特性是往程序中添加元数据的的机制。可能的特性目标:包括配件,类,接口,类成员等。参见AttributeTargets枚举的取值:All,assembly,class,constructor,Delegate,enum,Event,field,interface,method,module……可以通过ILDASM进行查看。反射(reflection)(retrospection回顾,内省)是应用程序读取元数据的过程。3特性是一个对象。表示与程序中某元素(或者特性的目标)相关联的数据。特性应用于其目标时,将特性信息放在紧贴目标之前的方括号中。[特性]特性目标一个目标可以有多个特性。42内置特性信息常见的内置特性信息包括:装配件的有关信息。Assemble:COM互操作SerializableDllImport在C#编写的代码中调用一个外部的其他工具开发的dll中的某函数。比如Win32API。Conditional条件编译特性。类似于C中的#if…#endif53自定义特性假设自定义一个特性用来记录代码的修改记录。以后可以写一个辅助程序来跟踪整个项目的代码修改情况,以便进行项目管理。在修改过的代码单元中使用如下的特性实例来记录:[BugFixAttribute(323,”Jesse”,”1/1/2005”,”abigbug”]该特性可能出现在类,类成员,接口等等位置。辅助程序读取程序中的元数据可以进行各种分类,统计等工作。(注释不能做到这点)6特性也是用类来描述的。所有的特性都派生自System.Attribute类。描述这个特性类要用到另一个预定义特性AttributeUsage。[AttributeUsage(

validon,

AllowMultiple=allowmultiple,

Inherited=inherited

)]

Validon指定属性可以放置在其上的语言元素(AttributeTargets值的组合)。默认值为AttributeTargets.All。多个值可以使用OR运算组合起来。AllowMultiple表示是否允许一个特性多次应用到同一个元素中。Inherited表示属性是否由派生类继承AttributeUsage是应用于属性信息的属性信息.称meta-attribute,它可以提供元元数据(meta-metadata)即关于元数据的数据.7[AttributeUsage(AttributeTargets.Class|AttributeTargets.Constructor|AttributeTargets.Field|AttributeTargets.Method|AttributeTargets.Property,//此特性可以使用的范围AllowMultiple=true)]//此特性可以多次重复使用publicclassBugFixAttribute:System.Attribute//特性类名字一般以Attribute为后缀。如果省略,编译器自动加上此后缀。{8

privateintbugID;privatestringcomment;privatestringdate;privatestringprogrammer;

publicBugFixAttribute(intbugID,stringprogrammer,stringdate){this.bugID=bugID;grammer=programmer;this.date=date;}//构造函数。9//propertyfornamedparameterpublicstringComment{get{returncomment;}set{comment=value;}}publicintBugID{get{returnbugID;}}publicstringDate{get{returndate;}}publicstringProgrammer{get{returnprogrammer;}}}10使用特性类来修饰其他类。[BugFixAttribute(121,"JesseLiberty","01/03/05")][BugFixAttribute(107,"JesseLiberty","01/04/05",Comment="Fixedoffbyoneerrors")]//两个特性的实例publicclassMyMath{publicdoubleDoFunc1(doubleparam1){returnparam1+DoFunc2(param1);}publicdoubleDoFunc2(doubleparam1){returnparam1/3;}}使用包含有特性的类:MyMathmm=newMyMath();Console.WriteLine("CallingDoFunc(7).Result:{0}",mm.DoFunc1(7));辅助程序可以读取这些特性信息。即反射114浏览元数据在运行期可以浏览带有特性的类的特性信息。辅助程序可以借此进行关于特性的操作。System.Reflection.MemberInfoinf=typeof(MyMath);//要引入带有特性的类的类型库。object[]attributes;attributes=inf.GetCustomAttributes(typeof(BugFixAttribute),false);//也要引入特性类的类型库。foreach(Objectattributeinattributes){BugFixAttributebfa=(BugFixAttribute)attribute;Console.WriteLine("\nBugID:{0}",bfa.BugID);Console.WriteLine("Programmer:{0}",bfa.Programmer);Console.WriteLine("Date:{0}",bfa.Date);Console.WriteLine("Comment:{0}",bfa.Comment);}125反射所有类型反射不仅可以查看元数据,也可以查看类型数据。如:可以用来对查询一个配件所包含的全部类型:

Assemblyb=Assembly.LoadFrom("classwithattr.dll");Type[]types=b.GetTypes(); MessageBox.Show(types.Length.ToString()); foreach(Typetintypes) {MessageBox.Show(t.Name);}136反射一个类型也可以只反射某一个指定的类型:

TypetheType=Type.GetType("System.Int32");Console.WriteLine("\nSingleTypeis{0}\n",theType);147反射类型的成员可以反射某类型的所有成员,包括方法,字段等。

MemberInfo[]mbrInfoArray=theType.GetMembers();foreach(MemberInfombrInfoinmbrInfoArray){Console.WriteLine("{0}isa{1}",mbrInfo,mbrInfo.MemberType);}或者使用GetMethods函数反射所有的方法,使用GetFilelds函数反射所有的字段等等。或者使用GetMethod函数反射指定的方法,使用GetFileld函数反射指定的字段等等。更一般地使用FindMembers来查找出具有某种特性的成员。详见msdn15寻找类型的方法

MemberInfo[]mbrInfoArray=theType.FindMembers(MemberTypes.Method,BindingFlags.Public|BindingFlags.Static|BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.DeclaredOnly,Type.FilterName,"Get*");foreach(MemberInfombrInfoinmbrInfoArray){Console.WriteLine("{0}isa{1}",mbrInfo,mbrInfo.MemberType);}168MemberInfoMemberInfo

类是用于获取类的所有成员(构造函数、事件、字段、方法和属性)信息的类的抽象基类。此类引入所有成员都提供的基本功能。FindMembers等函数返回一个MemberInfoMemberInfo是MethodInfo,FileldInfo等的基类。GetMember,GetFiled分别返回之。通过MethodInfo可以动态调用它所反映的方法。即迟绑定。179迟绑定早绑定编译时刻迟绑定(晚绑定)运行时刻。灵活性与性能的权衡。迟绑定用于不经过编译的场合,比如脚本语言。以下代码需usingSystem.Reflection;18TypetheMathType=Type.GetType("System.Math");Type[]paramTypes=newType[1];paramTypes[0]=Type.GetType("System.Double");

//查找名字为“Cos”,参数为paramTypes的函数,得到Cos的methodinfo名字“Cos”是猜测的。如猜测失败,虽然编译通过,但运行失败。MethodInfoCosineInfo=theMathType.GetMethod("Cos",paramTypes);//准备参数。Object[]parameters=newObject[1];parameters[0]=45*(Math.PI/180);//45度ObjectreturnVal=CosineInfo.Invoke(theMathType,parameters);//动态调用。Console.WriteLine("Thecosineofa45is{0}",returnVal);1910反射发送Reflectionemit支持运行期的类型动态创建例子:循环与硬编码BruteForceandLoopDynamicInvokeDynamicInterfaceReflectionEmit20publicintDoSum(intn)//n=20{intresult=0;for(inti=1;i<=n;i++){result+=i;}returnresult;}

//循环体中执行多条指令。publicintDoSum2(){return1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20;}//只执行一条指令。硬编码的方式比循环的方式快10倍。21intval=20,iterations=1000000; intresult=0; MyMathm=newMyMath(); DateTimestartTime=DateTime.Now; for(inti=0;i<iterations;i++)result=m.DoSum(val);//循环计算测试 TimeSpanelapsed= DateTime.Now-startTime; Console.WriteLine("Loop:Sumis{0}",result); Console.WriteLine("timeis:" +elapsed.TotalMilliseconds.ToString()); startTime=DateTime.Now; for(inti=0;i<iterations;i++) result=m.DoSum2();

//硬编码蛮力计算计算测试. elapsed=DateTime.Now-startTime; Console.WriteLine("BruteForce:Sumis{0}",result); Console.WriteLine("timeis:" + elapsed.TotalMilliseconds);2210.1动态调用运行时动态创建一个类,此类能用原始方法进行求和计算。此类在程序运行前并不存在,而是运行时刻,由程序根据用户的输入参数(求和数目)决定方法的实现方式,从而动态地写出其定义。类的定义动态完成后,调用编译器进行编译得到装配件。加载装配件。得到类的原始方法。动态调用此方法。有多种方法.23为此创建一个ReflectionTest类。publicclassReflectionTest{TypetheType=null;//用来保存任何类型,这里保存动态创建的类型。objecttheClass=null;//用来保存任何对象,这里保存动态创建的对象。

publicdoubleDoSum(inttheValue){……}

privatevoidGenerateCode(inttheVal){……}}24

publicdoubleDoSum(inttheValue){if(theType==null)//动态创建类型{GenerateCode(theValue);}//根据参数动态创建代码,启动新进程,编译代码,生成配件。主进程再加载配件,动态创建对象。object[]arguments=newobject[0];objectretVal=//动态调用:theType.InvokeMember("ComputeSum",BindingFlags.Default|BindingFlags.InvokeMethod,null,theClass,arguments);//或者由type-》GetMethod;MethodInfo-》Invokereturn(double)retVal;}25privatevoidGenerateCode(inttheVal) {

stringfileName="BruteForceSums"; Streams= File.Open(fileName+".cs",FileMode.Create); StreamWriterwrtr=newStreamWriter(s); stringclassName="BruteForceSums"; wrtr.WriteLine("class{0}",className); wrtr.WriteLine("{");

……//根据参数theVal,动态生成c#代码。wrtr.Close();s.Close();

//以下启动进程编译此代码,得到装配件26ProcessStartInfopsi=newProcessStartInfo();psi.FileName="cmd.exe";stringcompileString="/c{0}csc/optimize+";compileString+="/target:library";compileString+="{1}.cs>compile.out";stringframeworkDir=RuntimeEnvironment.GetRuntimeDirectory();psi.Arguments=String.Format(compileString,frameworkDir,fileName);psi.WindowStyle=ProcessWindowStyle.Minimized;Processproc=Process.Start(psi);//启动进程进行编译proc.WaitForExit(2000);//本进程等待2秒。//应该用进程的同步机制,这里是一种临时的处理方法27Assemblya=Assembly.LoadFrom(fileName+".dll");//加载装配件theClass=a.CreateInstance(className);//得到对象theType=a.GetType(className);//得到类File.Delete(fileName+“.cs”);//删除临时的装配件结果:使用动态调用的方式速度更慢。原因:未使用进程的同步机制。(使用同步机制会消除。)读写硬盘(使用Emit消除)迟绑定(使用接口消除)2810.2接口一个能加快速度的方法是把迟绑定改为早绑定。但如下方法是不行的:objecttheClass=null;theClass=a.CreateInstance(className);//得到对象theClass.ComputeSum();//theClass并不知晓ComputeSum为何物。

为此定义一个接口:publicinterfaceIComputer{doubleComputeSum();}29publicclassReflectionTest{//TypetheType=null;//用来保存任何类型,这里保存动态创建的类型。//objecttheClass=null;//用来保存任何对象,这里保存动态创建的对象。IComputertheComputer=null;//成员接口,以后将用来保存动态创建的对象,//此对象类实现了此接口。

publicdoubleDoSum(inttheValue){……}

privatevoidGenerateCode(inttheVal){……}}30publicdoubleDoSum(inttheValue){if(theComputer==null){GenerateCode(theValue);}return(theComputer.ComputeSum());}//不使用迟绑定,而通过接口来进行调用。31

privatevoidGenerateCode(inttheVal){……//生成代码,编译,同前,唯一要注意这里编译时要加入对ReflectionTest.exe的引用,以使得编译器知晓Icomputer的定义。

Assemblya=Assembly.LoadFrom(fileName+".dll");theComputer=(IComputer)a.CreateInstance(className);}性能会稍有改进。3210.3反射发送以上出现性能瓶颈的原因在于硬盘操作。进行如下的改进:publicinterfaceIComputer{intComputeSum();}publicclassReflectionTest{IComputertheComputer=null;publicdoubleDoSum(inttheValue){……}//同10.2完全一致。public

voidGenerateCode(inttheValue){AssemblytheAssembly=EmitAssembly(theValue);theComputer=(IComputer)theAssembly.CreateInstance("BruteForceSums");}privateAssemblyEmitAssembly(inttheValue){……}//动态创建对象的方法有重大改进!}

33privateAssemblyEmitAssembly(inttheValue){AssemblyNameAssemblyBuilderModuleBuilderTypeBuilderAddInterfaceImplementationMethodBuilderILGeneratorgenerator.Emit34

AssemblyNameassemblyName=newAssemblyName();assemblyName.Name=“DoSumAssembly”;//配件的名字AssemblyBuildernewAssembly=Thread.GetDomain().DefineDynamicAssembly(assemblyName,AssemblyBuilderAccess.Run);ModuleBuildernewModule=newAssembly.DefineDynamicModule("Sum");//生成单模块的配件TypeBuildermyType=newModule.DefineType("BruteForceSums",TypeAttributes.Public);//在配件中定义一个类myType.AddInterfaceImplementation(typeof(IComputer));//该类实现接口IComputer.

35//为类定义一个方法,方法具有具有我们希望的签名。Type[]paramTypes=newType[0];TypereturnType=typeof(int);MethodBuildersimpleMethod=myType.DefineMethod("ComputeSum",MethodAttributes.Public|MethodAttributes.Virtual,retu

温馨提示

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

最新文档

评论

0/150

提交评论