1、什么是反射

  Reflection,中文翻译为反射。  这是.Net中获取运行时类型信息的方式,.Net的应用程序由几个部分:

‘程序集(Assembly)’、‘模块(Module)’、‘类型(class)’组成,而反射提供一种编程的方式,让程序员可以在程序运行期获得这几个组成部分的相关信息,例如:

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

2、命名空间与装配件的关系

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

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

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

装配件A:

     namespace N1

     {

      public class AC1 {…}

      public class AC2 {…}

     }

    namespace N2

    {

       public class AC3 {…}

      public class AC4{…}

    }

装配件B:

     namespace N1

    {      

      public class BC1 {…}

      public class BC2 {…}

    }

    namespace N2

    {

      public class BC3 {…}

      public class BC4{…}

     }

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

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

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

3、运行期得到类型信息有什么用

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

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

    public interface IMediaFormat

    {

      string Extension {get;}

      Decoder GetDecoder();    

    }

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

4、如何使用反射获取类型

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

  …  public void Process( object processObj )

    {

      Type t = processsObj.GetType();

      if( t.GetInterface(“ITest”) !=null )  …

    }  …

  另外一种获取类型的方法是通过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://search.csdn.net/Expert/topic/2210/2210762.xml?temp=.1919977  qqchen的回答很精彩

5、如何根据类型来动态创建对象

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

  Type t = Type.GetType("System.Data.DataTable,System.Data,Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");

DataTable table = (DataTable)Activator.CreateInstance(t);

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

    namespace TestSpace

    {

      public class TestClass

       {

        private string _value;

        public TestClass(string value)

        {

           _value=value;

        }

      }

    }

      …

      Type t = Type.GetType(“TestSpace.TestClass”);

      Object[] constructParms = new object[] {“hello”};

      //构造器参数

      TestClass obj = (TestClass)Activator.CreateInstance(t,constructParms);

      …

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

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

    namespace TestSpace

     {

      public class TestClass

      {

        private string _value;

        public TestClass() {  }

        public TestClass(string value)

        {

          _value = value;

         }

      public string GetValue( string prefix )

       {

        if( _value==null )

           return "NULL";

         else

          return prefix+" : "+_value;

      }

      public string Value

      {

        set

        {

          _value=value;

        }

        get

         {

           if( _value==null )

            return "NULL";

          else

             return _value;

         }

       }

     }

  }

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

  如://获取类型信息

    Type t = Type.GetType("TestSpace.TestClass");

    //构造器的参数

    object[] constuctParms = new object[]{"timmy"};

    //根据类型创建对象

    object dObj = Activator.CreateInstance(t,constuctParms);

    //获取方法的信息

    MethodInfo method = t.GetMethod("GetValue");

     //调用方法的一些标志位,这里的含义是Public并且是实例方法,这也是默认的值

    BindingFlags flag = BindingFlags.Public | BindingFlags.Instance;

     //GetValue方法的参数

     object[] parameters = new object[]{"Hello"};

    //调用方法,用一个object接收返回值

     object returnValue = method.Invoke(dObj,flag,Type.DefaultBinder,parameters,null);

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

7、动态创建委托

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

  namespace TestSpace

  {

    delegate string TestDelegate(string value);

    public class TestClass

    {

       public TestClass() {  }

      public void GetValue(string value)

      {

         return value;

       }

    }

  } 
  使用示例:  TestClass obj = new TestClass(); 
  //获取类型,实际上这里也可以直接用typeof来获取类型

  Type t = Type.GetType(“TestSpace.TestClass”);

  //创建代理,传入类型、创建代理的对象以及方法名称

  TestDelegate method = (TestDelegate)Delegate.CreateDelegate(t,obj,”GetValue”);

  String returnValue = method(“hello”);

到这里,我们简单的讲述了反射的作用以及一些基本的用法,还有很多方面没有涉及到,有兴趣的朋友可以参考MSDN。

.net反射机制的简单介绍的更多相关文章

  1. java反射机制的简单介绍

    参考博客: https://blog.csdn.net/mlc1218559742/article/details/52754310 先给出反射机制中常用的几个方法: Class.forName (& ...

  2. (转)java反射机制及简单工厂模式

    第一步,定义一个接口类 package factory.face; /** * 接口,为了保证返回的对象可以统一用 Product接受 * @author Administrator */ publi ...

  3. 045-利用反射机制,简单的实现PHP插件模式

    <?php //利用反射机制,简单的实现PHP插件模式 # 假设,我们有一款开源产品,所有开发者都必须在我定制的需求之上,进行二次开发, # 而开发完成后的新模块,就是一个不一样的新插件,可以放 ...

  4. C++ 反射机制的简单实现

    C++并不支持反射机制,只能自己实现. 如果需要实现字字符串到函数到映射,一定要使用到函数指针. 简单实现反射机制,根据字符串来构造相应到类.主要有以下几点: (1) 可以使用map保存字符从到函数指 ...

  5. 利用Java反射机制优化简单工厂设计模式

    之前项目有个需求,审批流程的时候要根据配置发送信息:发送短信.发送邮件.当时看到这个就想到要用工厂模式,为什么要用工厂模式呢?用工厂模式进行大型项目的开发,可以很好的进行项目并行开发.就是一个程序员和 ...

  6. java反射机制的简单使用

    java 反射机制 反射机制简介 参考地址 什么是反射机制 反射机制指程序在运行时能够获取自身的信息.在java中只要给定类的名字,就可以通过反射机制获得类的所有信息 反射机制的优缺点 首先了解一下动 ...

  7. Java反射机制的简单学习

    今天看了一下Java的反射机制,就此记录一下. 首先,我们要先了解一下什么是反射? 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力. JAV ...

  8. Java反射机制的简单应用

    一直感觉java的反射机制非常强大,可是可用的地方不多.在android学习的时候.一直想实现挂断电话的功能,可是系统并没有提供开放的api接口,看了一下网上使用反射机制来实现该功能,确实非常强大,非 ...

  9. android Handler及消息处理机制的简单介绍

    学习android线程时,直接在UI线程中使用子线程来更新TextView显示的内容,会有如下错误:android.view.ViewRoot$CalledFromWrongThreadExcepti ...

随机推荐

  1. asbDio (asbDio.dll)

    using System;using System.Collections.Generic;using System.Text;using System.Runtime.InteropServices ...

  2. Hbase操作集锦

    一.Hbase架构与理解 Hbase架构大致如下,图片取自https://www.cnblogs.com/linkworld/p/10963910.html. 二.Hbase shell操作 创建Hb ...

  3. Topshelf 搭建 Windows 服务

    Topshelf 是一个用来部署基于.NET Framework 开发的服务的框架.简化服务创建于部署过程,并且支持控制台应用程序部署为服务.本文基于 .net core 控制台应用程序部署为服务(. ...

  4. python面试导航

    python面试题库 python基础 等待更新中 函数 等待更新中 面向对象 等待更新中 高级编程 等待更新中 数据库 等待更新中 前端&django 等待更新中 crm 等待更新中 drf ...

  5. Android -- SEGV_MAPERR,SEGV_ACCERR

    Per siginfo.h: SEGV_MAPERR means you tried to access an address that doesn’t map to anything. SEGV_A ...

  6. sqlalchemy 配合bootstrap-table实现后台分页

    创建公共的mysql连接函数 def db_session(): db_config='mysql+mysqldb://'+ mysqluser + ':' + mysqlpassword+'@'+m ...

  7. gitblit服务器:用户、团队、权限管理

    在日常开发工作中,我们通常使用版本控制软件管理团队的源代码,常用的SVN.Git.与SVN相比,Git有分支的概念,可以从主分支创建开发分支,在开发分支测试没有问题之后,再合并到主分支上去,从而避免了 ...

  8. 模型文件(checkpoint)对模型参数的储存与恢复

    1.  模型参数的保存: import tensorflow as tfw=tf.Variable(0.0,name='graph_w')ww=tf.Variable(tf.random_normal ...

  9. SpringBoot构建RESTful API

    1.RESTful介绍 RESTful是一种软件架构风格! RESTful架构风格规定,数据的元操作,即CRUD(create, read, update和delete,即数据的增删查改)操作,分别对 ...

  10. chrome截屏的方法

    原文本文链接:https://blog.csdn.net/xiaofengzhiyu/article/details/94652057 Chrome保存整个网页为图片保存为图片右键检查快捷键Ctrl+ ...