zoukankan      html  css  js  c++  java
  • C#中的反射

    反射指程序可以访问,检测和修改它本身状态或行为的一种能力。

    程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。

    可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以

    调用类型的方法或访问其字段和属性。

    优点:

         1、反射提高了程序的灵活性和扩展性。

          2、降低耦合性,提高自适应能力。

          3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

    缺点:

          1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。

    因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。

          2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码

    的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

    反射用途:

          1、允许在运行时查看属性(attrubute)信息。

          2、允许审查集合中的各种类型及实例化这些类型。

          3、允许延迟绑定的方法和属性(property).

          4、允许在运行时创建新类型然后使用这些类型执行一些任务。

    查看元数据

    使用反射(Reflection)可以查看属性(attribute)信息。

    System.Reflection 类的 MemberInfo 对象需要被初始化,用于发现与类相关的属性(attribute)。为了做到这点,您可以定义目标类的一个对象,如下:

    System.Reflection.MemberInfo info = typeof(MyClass);

    下面分享几篇关于反射的博文:
    第一篇:
    详解C#中的反射

    http://blog.csdn.net/educast/article/details/2894892

    两个现实中的例子:
    1、B超:大家体检的时候大概都做过B超吧,B超可以透过肚皮探测到你内脏的生理情况。这是如何做到的呢?B超是B型超声波,它可以透过肚皮通过向你体内发射B型超声波,当超声波遇到内脏壁的时候就会产生一定的“回音”反射,然后把“回音”进行处理就可以显示出内脏的情况了(我不是医生也不是声学专家,不知说得是否准确^_^)。
    2、地球内部结构:地球的内部结构大体可以分为三层:地壳、地幔和地核。地壳是固体,地核是液体,地幔则是半液半固的结构(中学地理的内容,大家还记得吧?)。如何在地球表面不用深入地球内部就知道其内部的构造呢?对,向地球发射“地震波”,“地震波”分两种一种是“横波”,另一种是“纵波”。“横波”只能穿透固体,而“纵波”既可穿透固体又可以穿透液体。通过在地面对纵波和横波的反回情况,我们就可以大体断定地球内部的构造了。

    大家注意到这两个例子的共同特点,就是从一个对象的外部去了解对象内部的构造,而且都是利用了波的反射功能。在.NET中的反射也可以实现从对象的外部来了解对象(或程序集)内部结构的功能,哪怕你不知道这个对象(或程序集)是个什么东西,另外.NET中的反射还可以运态创建出对象并执行它其中的方法。

    反射是.NET中的重要机制,通过反射,可以在运行时获得程序或程序集中每一个类型(包括类、结构、委托、接口和枚举等)的成员和成员的信息。有了反射,即可对每一个类型了如指掌。另外我还可以直接创建对象,即使这个对象的类型在编译时还不知道。 
        
    反射的用途:
        (1)使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。 
        (2)使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。 
        (3)使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。 
        (4)使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。
        (5)使用FiedInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。
        (6)使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。 
        (7)使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。 
        (8)使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。

    反射用到的命名空间:
        System.Reflection
        System.Type
        System.Reflection.Assembly
        
    反射用到的主要类:
        System.Type 类--通过这个类可以访问任何给定数据类型的信息。
        System.Reflection.Assembly类--它可以用于访问给定程序集的信息,或者把这个程序集加载到程序中。
        
    System.Type类:
        System.Type 类对于反射起着核心的作用。但它是一个抽象的基类,Type有与每种数据类型对应的派生类,我们使用这个派生类的对象的方法、字段、属性来查找有关该类型的所有信息。
        获取给定类型的Type引用有3种常用方式:
        ●使用 C# typeof 运算符。
            Type t = typeof(string);
        ●使用对象GetType()方法。
            string s = "grayworm";
            Type t = s.GetType(); 
        ●还可以调用Type类的静态方法GetType()。
            Type t = Type.GetType("System.String");
           
        上面这三类代码都是获取string类型的Type,在取出string类型的Type引用t后,我们就可以通过t来探测string类型的结构了。 
                string n = "grayworm";
                Type t = n.GetType();
                foreach (MemberInfo mi in t.GetMembers())
                {
                    Console.WriteLine("{0}/t{1}",mi.MemberType,mi.Name);
                }
        
        Type类的属性:
            Name 数据类型名
            FullName 数据类型的完全限定名(包括命名空间名)
            Namespace 定义数据类型的命名空间名
            IsAbstract 指示该类型是否是抽象类型
            IsArray   指示该类型是否是数组
            IsClass   指示该类型是否是类
            IsEnum   指示该类型是否是枚举
            IsInterface    指示该类型是否是接口
            IsPublic 指示该类型是否是公有的
            IsSealed 指示该类型是否是密封类
            IsValueType 指示该类型是否是值类型
        Type类的方法:
            GetConstructor(), GetConstructors():返回ConstructorInfo类型,用于取得该类的构造函数的信息
            GetEvent(), GetEvents():返回EventInfo类型,用于取得该类的事件的信息
            GetField(), GetFields():返回FieldInfo类型,用于取得该类的字段(成员变量)的信息
            GetInterface(), GetInterfaces():返回InterfaceInfo类型,用于取得该类实现的接口的信息
            GetMember(), GetMembers():返回MemberInfo类型,用于取得该类的所有成员的信息
            GetMethod(), GetMethods():返回MethodInfo类型,用于取得该类的方法的信息
            GetProperty(), GetProperties():返回PropertyInfo类型,用于取得该类的属性的信息
        可以调用这些成员,其方式是调用Type的InvokeMember()方法,或者调用MethodInfo, PropertyInfo和其他类的Invoke()方法。 
        
        查看类中的构造方法:
            NewClassw nc = new NewClassw();
            Type t = nc.GetType();
            ConstructorInfo[] ci = t.GetConstructors();    //获取类的所有构造函数
            foreach (ConstructorInfo c in ci) //遍历每一个构造函数
            {
                ParameterInfo[] ps = c.GetParameters();    //取出每个构造函数的所有参数
                foreach (ParameterInfo pi in ps)   //遍历并打印所该构造函数的所有参数
                {
                    Console.Write(pi.ParameterType.ToString()+" "+pi.Name+",");
                }
                Console.WriteLine();
            }
        
        用构造函数动态生成对象:
            Type t = typeof(NewClassw);
            Type[] pt = new Type[2];
            pt[0] = typeof(string);
            pt[1] = typeof(string);
            //根据参数类型获取构造函数 
            ConstructorInfo ci = t.GetConstructor(pt); 
            //构造Object数组,作为构造函数的输入参数 
            object[] obj = new object[2]{"grayworm","hi.baidu.com/grayworm"};   
            //调用构造函数生成对象 
            object o = ci.Invoke(obj);    
            //调用生成的对象的方法测试是否对象生成成功 
            //((NewClassw)o).show();    
        
        用Activator生成对象:
            Type t = typeof(NewClassw);
            //构造函数的参数 
            object[] obj = new object[2] { "grayworm", "hi.baidu.com/grayworm" };   
            //用Activator的CreateInstance静态方法,生成新对象 
            object o = Activator.CreateInstance(t,"grayworm","hi.baidu.com/grayworm"); 
            //((NewClassw)o).show();

        查看类中的属性:
            NewClassw nc = new NewClassw();
            Type t = nc.GetType();
            PropertyInfo[] pis = t.GetProperties();
            foreach(PropertyInfo pi in pis)
            {
                Console.WriteLine(pi.Name);
            }
        
        查看类中的public方法:
            NewClassw nc = new NewClassw();
            Type t = nc.GetType();
            MethodInfo[] mis = t.GetMethods();
            foreach (MethodInfo mi in mis)
            {
                Console.WriteLine(mi.ReturnType+" "+mi.Name);
            }
        
        查看类中的public字段
            NewClassw nc = new NewClassw();
            Type t = nc.GetType();
            FieldInfo[] fis = t.GetFields();
            foreach (FieldInfo fi in fis)
            {
                Console.WriteLine(fi.Name);
            } (http://hi.baidu.com/grayworm)
           
        用反射生成对象,并调用属性、方法和字段进行操作 
            NewClassw nc = new NewClassw();
            Type t = nc.GetType();
            object obj = Activator.CreateInstance(t);
            //取得ID字段 
            FieldInfo fi = t.GetField("ID");
            //给ID字段赋值 
            fi.SetValue(obj, "k001");
            //取得MyName属性 
            PropertyInfo pi1 = t.GetProperty("MyName");
            //给MyName属性赋值 
            pi1.SetValue(obj, "grayworm", null);
            PropertyInfo pi2 = t.GetProperty("MyInfo");
            pi2.SetValue(obj, "hi.baidu.com/grayworm", null);
            //取得show方法 
            MethodInfo mi = t.GetMethod("show");
            //调用show方法 
            mi.Invoke(obj, null);
            
    System.Reflection.Assembly类 
         Assembly类可以获得程序集的信息,也可以动态的加载程序集,以及在程序集中查找类型信息,并创建该类型的实例。
        使用Assembly类可以降低程序集之间的耦合,有利于软件结构的合理化。
        
        通过程序集名称返回Assembly对象
            Assembly ass = Assembly.Load("ClassLibrary831");
        通过DLL文件名称返回Assembly对象
            Assembly ass = Assembly.LoadFrom("ClassLibrary831.dll");
        通过Assembly获取程序集中类 
            Type t = ass.GetType("ClassLibrary831.NewClass");   //参数必须是类的全名
        通过Assembly获取程序集中所有的类
            Type[] t = ass.GetTypes();
           
        //通过程序集的名称反射
        Assembly ass = Assembly.Load("ClassLibrary831");
        Type t = ass.GetType("ClassLibrary831.NewClass");
        object o = Activator.CreateInstance(t, "grayworm", "http://hi.baidu.com/grayworm");
        MethodInfo mi = t.GetMethod("show");
        mi.Invoke(o, null);

       //通过DLL文件全名反射其中的所有类型
        Assembly assembly = Assembly.LoadFrom("xxx.dll的路径");
        Type[] aa = a.GetTypes();

        foreach(Type t in aa)
        {
            if(t.FullName == "a.b.c")
            {
                object o = Activator.CreateInstance(t);
            }
        }

    第二篇:
    [整理]C#反射(Reflection)详解
    
    

    本人理解:

    装配件:Assembly(程序集)

    晚绑定:后期绑定

    MSDN:反射(C# 编程指南)

    -----------------原文如下--------

    1、 什么是反射
    2、 命名空间与装配件的关系
    3、 运行期得到类型信息有什么用
    4、 如何使用反射获取类型
    5、 如何根据类型来动态创建对象
    6、 如何获取方法以及动态调用方法
    7、 动态创建委托

    1、什么是反射
            Reflection,中文翻译为反射。
            这是.Net中获取运行时类型信息的方式,.Net的应用程序由几个部分:‘程序集(Assembly)’、‘模块(Module)’、‘类型(class)’组成,而反射提供一种编程的方式,让程序员可以在程序运行期获得这几个组成部分的相关信息,例如:

            Assembly类可以获得正在运行的装配件信息,也可以动态的加载装配件,以及在装配件中查找类型信息,并创建该类型的实例。
    Type类可以获得对象的类型信息,此信息包含对象的所有要素:方法、构造器、属性等等,通过Type类可以得到这些要素的信息,并且调用之。
    MethodInfo包含方法的信息,通过这个类可以得到方法的名称、参数、返回值等,并且可以调用之。
    诸如此类,还有FieldInfo、EventInfo等等,这些类都包含在System.Reflection命名空间下。

    2、命名空间与装配件的关系
            很多人对这个概念可能还是很不清晰,对于合格的.Net程序员,有必要对这点进行澄清。
            命名空间类似与Java的包,但又不完全等同,因为Java的包必须按照目录结构来放置,命名空间则不需要。

            装配件是.Net应用程序执行的最小单位,编译出来的.dll、.exe都是装配件。

            装配件和命名空间的关系不是一一对应,也不互相包含,一个装配件里面可以有多个命名空间,一个命名空间也可以在多个装配件中存在,这样说可能有点模糊,举个例子:
    装配件A:

    1. namespace  N1
    2. {
    3.       public  class  AC1  {…}
    4.       public  class  AC2  {…}
    5. }
    6. namespace  N2
    7. {
    8.       public  class  AC3  {…}
    9.       public  class  AC4{…}
    10. }
    复制代码

    装配件B:

    1. namespace  N1
    2. {
    3.       public  class  BC1  {…}
    4.       public  class  BC2  {…}
    5. }
    6. namespace  N2
    7. {
    8.       public  class  BC3  {…}
    9.       public  class  BC4{…}
    10. }
    复制代码

    这两个装配件中都有N1和N2两个命名空间,而且各声明了两个类,这样是完全可以的,然后我们在一个应用程序中引用装配件A,那么在这个应用程序中,我们能看到N1下面的类为AC1和AC2,N2下面的类为AC3和AC4。
            接着我们去掉对A的引用,加上对B的引用,那么我们在这个应用程序下能看到的N1下面的类变成了BC1和BC2,N2下面也一样。
            如果我们同时引用这两个装配件,那么N1下面我们就能看到四个类:AC1、AC2、BC1和BC2。

            到这里,我们可以清楚一个概念了,命名空间只是说明一个类型是那个族的,比如有人是汉族、有人是回族;而装配件表明一个类型住在哪里,比如有人住在北京、有人住在上海;那么北京有汉族人,也有回族人,上海有汉族人,也有回族人,这是不矛盾的。

            上面我们说了,装配件是一个类型居住的地方,那么在一个程序中要使用一个类,就必须告诉编译器这个类住在哪儿,编译器才能找到它,也就是说必须引用该装配件。
            那么如果在编写程序的时候,也许不确定这个类在哪里,仅仅只是知道它的名称,就不能使用了吗?答案是可以,这就是反射了,就是在程序运行的时候提供该类型的地址,而去找到它。
    有兴趣的话,接着往下看吧。

    3、运行期得到类型信息有什么用
            有人也许疑问,既然在开发时就能够写好代码,干嘛还放到运行期去做,不光繁琐,而且效率也受影响。
    这就是个见仁见智的问题了,就跟早绑定和晚绑定一样,应用到不同的场合。有的人反对晚绑定,理由是损耗效率,但是很多人在享受虚函数带来的好处的时侯还没有意识到他已经用上了晚绑定。这个问题说开去,不是三言两语能讲清楚的,所以就点到为止了。
            我的看法是,晚绑定能够带来很多设计上的便利,合适的使用能够大大提高程序的复用性和灵活性,但是任何东西都有两面性,使用的时侯,需要再三衡量。

    接着说,运行期得到类型信息到底有什么用呢?
    还是举个例子来说明,很多软件开发者喜欢在自己的软件中留下一些接口,其他人可以编写一些插件来扩充软件的功能,比如我有一个媒体播放器,我希望以后可以很方便的扩展识别的格式,那么我声明一个接口:

    1. public  interface  IMediaFormat
    2. {
    3. string  Extension  {get;}
    4. Decoder  GetDecoder();
    5. }
    复制代码

    这个接口中包含一个Extension属性,这个属性返回支持的扩展名,另一个方法返回一个解码器的对象(这里我假设了一个Decoder的类,这个类提供把文件流解码的功能,扩展插件可以派生之),通过解码器对象我就可以解释文件流。
    那么我规定所有的解码插件都必须派生一个解码器,并且实现这个接口,在GetDecoder方法中返回解码器对象,并且将其类型的名称配置到我的配置文件里面。
    这样的话,我就不需要在开发播放器的时侯知道将来扩展的格式的类型,只需要从配置文件中获取现在所有解码器的类型名称,而动态的创建媒体格式的对象,将其转换为IMediaFormat接口来使用。

    这就是一个反射的典型应用。


    4、如何使用反射获取类型
            首先我们来看如何获得类型信息。
            获得类型信息有两种方法,一种是得到实例对象
            这个时侯我仅仅是得到这个实例对象,得到的方式也许是一个object的引用,也许是一个接口的引用,但是我并不知道它的确切类型,我需要了解,那么就可以通过调用System.Object上声明的方法GetType来获取实例对象的类型对象,比如在某个方法内,我需要判断传递进来的参数是否实现了某个接口,如果实现了,则调用该接口的一个方法:

    1. public  void  Process(  object  processObj  )
    2. {
    3. Type  t  =  processsObj.GetType();
    4. if(  t.GetInterface(“ITest”)  !=null  )
    5.                     …
    6. }
    复制代码

    另外一种获取类型的方法是通过Type.GetType以及Assembly.GetType方法,如:
                  Type  t  =  Type.GetType(“System.String”);
            需要注意的是,前面我们讲到了命名空间和装配件的关系,要查找一个类,必须指定它所在的装配件,或者在已经获得的Assembly实例上面调用GetType。
            本装配件中类型可以只写类型名称,另一个例外是mscorlib.dll,这个装配件中声明的类型也可以省略装配件名称(.Net装配件编译的时候,默认都引用了mscorlib.dll,除非在编译的时候明确指定不引用它),比如:
              System.String是在mscorlib.dll中声明的,上面的Type  t  =  Type.GetType(“System.String”)是正确的
              System.Data.DataTable是在System.Data.dll中声明的,那么:
    Type.GetType(“System.Data.DataTable”)就只能得到空引用。
              必须:
    Type  t  =  Type.GetType("System.Data.DataTable,System.Data,Version=1.0.3300.0,  Culture=neutral,  PublicKeyToken=b77a5c561934e089");
              这样才可以,大家可以看下面这个帖子:
                    http://expert.csdn.net/Expert/to ... 2.xml?temp=.1919977
              qqchen的回答很精彩


    5、如何根据类型来动态创建对象
            System.Activator提供了方法来根据类型动态创建对象,比如创建一个DataTable:

    1. Type  t  =  Type.GetType("System.Data.DataTable,System.Data,Version=1.0.3300.0,  Culture=neutral,  PublicKeyToken=b77a5c561934e089");
    2. DataTable  table  =  (DataTable)Activator.CreateInstance(t);
    复制代码

    例二:根据有参数的构造器创建对象

    1. namespace  TestSpace  
    2. {
    3.   public  class  TestClass
    4.       {
    5.       private  string  _value;
    6.       public  TestClass(string  value)  
    7.     {
    8.       _value=value;
    9.       }
    10.   }
    11. }
    12. Type  t  =  Type.GetType(“TestSpace.TestClass”);
    13. Object[]  constructParms  =  new  object[]  {“hello”};  //构造器参数
    14. TestClass  obj  =  (TestClass)Activator.CreateInstance(t,constructParms);
    复制代码

    把参数按照顺序放入一个Object数组中即可


    6、如何获取方法以及动态调用方法

    1. namespace  TestSpace
    2. {
    3.       public  class  TestClass  {
    4.           private  string  _value;
    5.           public  TestClass()  {
    6.           }
    7.           public  TestClass(string  value)  {
    8.                 _value  =  value;
    9.           }
    10.           public  string  GetValue(  string  prefix  )  {
    11.           if(  _value==null  )
    12.           return  "NULL";
    13.           else
    14.             return  prefix+"  :  "+_value;
    15.             }
    16.             public  string  Value  {
    17. set  {
    18. _value=value;
    19. }
    20. get  {
    21. if(  _value==null  )
    22. return  "NULL";
    23. else
    24. return  _value;
    25. }
    26.             }
    27.       }
    28. }
    复制代码

    上面是一个简单的类,包含一个有参数的构造器,一个GetValue的方法,一个Value属性,我们可以通过方法的名称来得到方法并且调用之,如:

    1. //获取类型信息
    2. Type  t  =  Type.GetType("TestSpace.TestClass");
    3. //构造器的参数
    4. object[]  constuctParms  =  new  object[]{"timmy"};
    5. //根据类型创建对象
    6. object  dObj  =  Activator.CreateInstance(t,constuctParms);
    7. //获取方法的信息
    8. MethodInfo  method  =  t.GetMethod("GetValue");
    9. //调用方法的一些标志位,这里的含义是Public并且是实例方法,这也是默认的值
    10. BindingFlags  flag  =  BindingFlags.Public  |  BindingFlags.Instance;
    11. //GetValue方法的参数
    12. object[]  parameters  =  new  object[]{"Hello"};
    13. //调用方法,用一个object接收返回值
    14. object  returnValue  =  method.Invoke(dObj,flag,Type.DefaultBinder,parameters,null);
    复制代码

    属性与方法的调用大同小异,大家也可以参考MSDN

    7、动态创建委托
            委托是C#中实现事件的基础,有时候不可避免的要动态的创建委托,实际上委托也是一种类型:System.Delegate,所有的委托都是从这个类派生的
            System.Delegate提供了一些静态方法来动态创建一个委托,比如一个委托:

    1. namespace  TestSpace  {
    2.       delegate  string  TestDelegate(string  value);
    3.       public  class  TestClass  {
    4. public  TestClass()  {
    5.                   }
    6.                   public  void  GetValue(string  value)  {
    7.                           return  value;
    8.                   }
    9.         }
    10. }
    复制代码

    使用示例:

    1. TestClass  obj  =  new  TestClass();
    2. //获取类型,实际上这里也可以直接用typeof来获取类型
    3. Type  t  =  Type.GetType(“TestSpace.TestClass”);
    4. //创建代理,传入类型、创建代理的对象以及方法名称
    5. TestDelegate  method  =  (TestDelegate)Delegate.CreateDelegate(t,obj,”GetValue”);
    6. String  returnValue  =  method(“hello”);
    复制代码

    ---------------------------------------------------------------------------------

    另外一篇关于反射的文章

    ---------------原文如下------------------

    反射的定义:审查元数据并收集关于它的类型信息的能力。元数据(编译以后的最基本数据单元)就是一大堆的表,当编译程序集或者模块时,编译器会创建一个类定义表,一个字段定义表,和一个方法定义表等。           System.reflection命名空间包含的几个类,允许你反射(解析)这些元数据表的代码   
    System.Reflection.Assembly  System.Reflection.MemberInfo System.Reflection.EventInfo System.Reflection.FieldInfo System.Reflection.MethodBase System.Reflection.ConstructorInfo System.Reflection.MethodInfo System.Reflection.PropertyInfo System.Type 以下是上面几个类的使用方法: (1)使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。  (2)使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。  (3)使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetConstructors或 GetConstructor方法来调用特定的构造函数。  (4)使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法来调用特定的方法。  (5)使用FiedInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。  (6)使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。  (7)使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。  (8)使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。 反射的层次模型: (注:层次间都是一对多的关系)
     

    反射的作用: 1、可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型 2、应用程序需要在运行时从某个特定的程序集中载入一个特定的类型,以便实现某个任务时可以用到反射。 3、反射主要应用与类库,这些类库需要知道一个类型的定义,以便提供更多的功能。

    应用要点: 1、现实应用程序中很少有应用程序需要使用反射类型 2、使用反射动态绑定需要牺牲性能 3、有些元数据信息是不能通过反射获取的 4、某些反射类型是专门为那些clr 开发编译器的开发使用的,所以你要意识到不是所有的反射类型都是适合每个人的。

    反射appDomain 的程序集:

    当你需要反射AppDomain 中包含的所有程序集,示例如下: static void Main {        //通过GetAssemblies 调用appDomain的所有程序集        foreach (Assembly assem in Appdomain.currentDomain.GetAssemblies())       {        //反射当前程序集的信息             reflector.ReflectOnAssembly(assem)       } }

    说明:调用AppDomain 对象的GetAssemblies 方法 将返回一个由System.Reflection.Assembly元素组成的数组。

    反射单个程序集:

    上面的方法讲的是反射AppDomain的所有程序集,我们可以显示的调用其中的一个程序集,system.reflecton.assembly 类型提供了下面三种方法: 1、Load 方法:极力推荐的一种方法,Load 方法带有一个程序集标志并载入它,Load 将引起CLR把策略应用到程序集上,先后在全局程序集缓冲区,应用程序基目录和私有路径下面查找该程序集,如果找不到该程序集系统抛出异常 2、LoadFrom 方法:传递一个程序集文件的路径名(包括扩展名),CLR会载入您指定的这个程序集,传递的这个参数不能包含任何关于版本号的信息,区域性,和公钥信息,如果在指定路径找不到程序集抛出异常。 3、LoadWithPartialName:永远不要使用这个方法,因为应用程序不能确定再在载入的程序集的版本。该方法的唯一用途是帮助那些在.Net框架的测试环节使用.net 框架提供的某种行为的客户,这个方法将最终被抛弃不用。

    注意:system.AppDomain 也提供了一种Load 方法,他和Assembly的静态Load 方法不一样,AppDomain的load 方法是一种实例方法,返回的是一个对程序集的引用,Assembly的静态Load 方发将程序集按值封装发回给发出调用的AppDomain.尽量避免使用AppDomain的load 方法

    利用反射获取类型信息:

    前面讲完了关于程序集的反射,下面在讲一下反射层次模型中的第三个层次,类型反射 一个简单的利用反射获取类型信息的例子:

    using system; using sytem.reflection; class reflecting  {        static void Main(string[]args)        {              reflecting reflect=new reflecting();//定义一个新的自身类              //调用一个reflecting.exe程序集

                 assembly myAssembly =assembly.loadfrom(“reflecting.exe”)              reflect.getreflectioninfo(myAssembly);//获取反射信息        }

           //定义一个获取反射内容的方法        void getreflectioninfo(assembly myassembly)        {              type[] typearr=myassemby.Gettypes();//获取类型              foreach (type type in typearr)//针对每个类型获取详细信息             {                    //获取类型的结构信息                   constructorinfo[] myconstructors=type.GetConstructors;

                     //获取类型的字段信息                  fieldinfo[] myfields=type.GetFiedls()

                     //获取方法信息                  MethodInfo   myMethodInfo=type.GetMethods();

                     //获取属性信息                  propertyInfo[] myproperties=type.GetProperties

                     //获取事件信息                  EventInfo[] Myevents=type.GetEvents;            }       } } 其它几种获取type对象的方法: 1、System.type   参数为字符串类型,该字符串必须指定类型的完整名称(包括其命名空间) 2、System.type 提供了两个实例方法:GetNestedType,GetNestedTypes 3、Syetem.Reflection.Assembly 类型提供的实例方法是:GetType,GetTypes,GetExporedTypes 4、System.Reflection.Moudle 提供了这些实例方法:GetType,GetTypes,FindTypes

    设置反射类型的成员: 反射类型的成员就是反射层次模型中最下面的一层数据。我们可以通过type对象的GetMembers 方法取得一个类型的成员。如果我们使用的是不带参数的GetMembers,它只返回该类型的公共定义的静态变量和实例成员,我们也可以通过使用带参数的 GetMembers通过参数设置来返回指定的类型成员。具体参数参考msdn 中system.reflection.bindingflags 枚举类型的详细说明。

    例如: //设置需要返回的类型的成员内容 bindingFlags bf=bingdingFlags.DeclaredOnly|bingdingFlags.Nonpublic|BingdingFlags.Public; foreach (MemberInfo mi int t.getmembers(bf)) {        writeline(mi.membertype)    //输出指定的类型成员 }

    通过反射创建类型的实例:

    通过反射可以获取程序集的类型,我们就可以根据获得的程序集类型来创建该类型新的实例,这也是前面提到的在运行时创建对象实现晚绑定的功能 我们可以通过下面的几个方法实现: 1、System.Activator 的CreateInstance方法。该方法返回新对象的引用。具体使用方法参见msdn 2、System.Activator 的createInstanceFrom 与上一个方法类似,不过需要指定类型及其程序集 3、System.Appdomain 的方法:createInstance,CreateInstanceAndUnwrap,CreateInstranceFrom和CreateInstraceFromAndUnwrap 4、System.type的InvokeMember实例方法:这个方法返回一个与传入参数相符的构造函数,并构造该类型。 5、System.reflection.constructinfo 的Invoke实例方法

    反射类型的接口:

    如果你想要获得一个类型继承的所有接口集合,可以调用Type的FindInterfaces GetInterface或者GetInterfaces。所有这些方法只能返回该类型直接继承的接口,他们不会返回从一个接口继承下来的接口。要想返回接口的基础接口必须再次调用上述方法。

    反射的性能:

    使用反射来调用类型或者触发方法,或者访问一个字段或者属性时clr 需要做更多的工作:校验参数,检查权限等等,所以速度是非常慢的。所以尽量不要使用反射进行编程,对于打算编写一个动态构造类型(晚绑定)的应用程序,可以采取以下的几种方式进行代替: 1、通过类的继承关系。让该类型从一个编译时可知的基础类型派生出来,在运行时生成该类型的一个实例,将对其的引用放到其基础类型的一个变量中,然后调用该基础类型的虚方法。 2、通过接口实现。在运行时,构建该类型的一个实例,将对其的引用放到其接口类型的一个变量中,然后调用该接口定义的虚方法。 3、通过委托实现。让该类型实现一个方法,其名称和原型都与一个在编译时就已知的委托相符。在运行时先构造该类型的实例,然后在用该方法的对象及名称构造出该委托的实例,接着通过委托调用你想要的方法。这个方法相对与前面两个方法所作的工作要多一些,效率更低一些。

    个人操作方案:

    源DLL类:

    using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using System.Web.UI; using System.Collections;
    namespace cn.SwordYang {
        public class TextClass:System.Web.UI.Page     {

    public static void RunJs(Page _page, string Source)         {             _page.ClientScript.RegisterStartupScript(_page.GetType(), "", "<script type="text/javascript">" + Source + ";</script>");
            }

    }

    }

    //调用代码

    System.Reflection.Assembly ass = Assembly.LoadFrom(Server.MapPath("bin/swordyang.dll")); //加载DLL             System.Type t = ass.GetType("cn.SwordYang.TextClass");//获得类型             object o = System.Activator.CreateInstance(t);//创建实例
                System.Reflection.MethodInfo mi = t.GetMethod("RunJs");//获得方法
                mi.Invoke(o, new object[] { this.Page,"alert('测试反射机制')"});//调用方法

    反射机制对应设计模式中的策略模式。

    
    
    
  • 相关阅读:
    洛谷 P1202 [USACO1.1]黑色星期五Friday the Thirteenth 题解
    洛谷 P1957 口算练习题 题解
    洛谷 P2036 Perket 题解
    洛谷 P2369 EXCEEDED WARNING A 题解
    洛谷 P1184高手之在一起 题解
    洛谷 P1897电梯里的爱情 题解
    Python做的眼睛护士
    Python-Tkinter的Entry详解
    Python的TkinterButton做为父窗口
    Python用Tkinter的Frame实现眼睛护士的倒计时黑色屏幕
  • 原文地址:https://www.cnblogs.com/newcapecjmc/p/7019435.html
Copyright © 2011-2022 走看看