• C# 反射学习


    1.什么是反射(Reflection)

    反射对于个人理解就是动态获取程序集的信息的方式。编译出来的.dll、.exe都是程序集。而程序集又包括:模块,而模块包含类型,类型又包含成员。而反射提供了一种方式,可以让程序在运行期间获取这些类型的信息。

    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接口来使用。

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

    优缺点

    优点:

    • 1、反射提高了程序的灵活性和扩展性。
    • 2、降低耦合性,提高自适应能力。
    • 3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

    缺点:

    • 1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
    • 2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

    反射(Reflection)的用途

    反射(Reflection)有下列用途:

    • 它允许在运行时查看属性(attribute)信息。
    • 它允许审查集合中的各种类型,以及实例化这些类型。
    • 它允许延迟绑定的方法和属性(property)。
    • 它允许在运行时创建新类型,然后使用这些类型执行一些任务。

    未完待续....

  • 相关阅读:
    VUE的生命周期
    ID生成算法(二)
    ID生成算法(一)——雪花算法
    HTTP状态码和支持的方法
    水平居中/垂直居中/水平垂直居中总结
    判断数组类型的4种方法
    WebSocket浅谈
    vue中使用定时器时this指向
    银行转账业务梳理
    支付那些事儿
  • 原文地址:https://www.cnblogs.com/zxk3113/p/6525402.html
Copyright © 2020-2023  润新知