反射的一些探索

动态加载程序集

在我尝试搭建一个高度抽象的企业级三层架构时,我运用了反射的原理来解除框架间层次的耦合,有兴趣的朋友可以点击这里,我的核心代码如下

using IDal;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Factory
{
    public class OrderDALFactory
    {

        private static readonly string AssemblyName = ConfigurationManager.AppSettings["Assembly"];
        private static readonly string className = ConfigurationManager.AppSettings["className"];
        public static IOrder CreateOrder()
        {
            return (IOrder)Assembly.Load(AssemblyName).CreateInstance(className);
        }
    }
}

通过读取配置文件中的程序集路径和类的名称,利用反射动态的加载程序集并创建实例,从而达到解耦的目的,但是当我运行程序的时候,抛出了一个异常,大致的意思是无法加载到该程序集。这就引起了我的一点思考,什么样的程序集才能被反射的Load()方法加载,通过查找资料我大致明白程序集加载的一个套路:

   1.对于强类型称的程序集(也就是除了程序集名称,还有一大堆用来唯一标识它的东西,如:MyType, Version=1.0.3087.28686, Culture=neutral, PublicKeyToken=337642649f453c2c)的加载会首先会访问全局程序集缓存(可以暂时理解文微软类库程序集存放的地方),如果没有找到则访问我们应用程序的工作目录,可以理解为程序的bin文件夹,如果还是没有找到则去私有目录找,至于私有目录则是我们用户自定义给程序的目录文件,我们可以在配置文件中配置,如下配置我们则指定了在当前程序根目录下的Modules文件夹为私有目录。

<configuration>
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <probing privatePath="Modules"></probing>
        </assemblyBinding>
    </runtime>
</configuration>

2.对于弱类型名称程序集(即除去标识只有名称的程序集如:MyType)则直接忽视全局程序集缓存的查找,直接找工作目录和私有目录,如果找的到则加载进来。

因此,很显然要解决我们程序集无法加载的异常只要把待加载的程序集在生成时的路径设置为当前应用的Bin文件夹就能解决,换句话说就是Bin文件夹里面必须要有待加载的程序集,不管该程序集有没有被引用。

我为什么要把反射动态加载程序集以及创建实例,拿出来单独讲,是因为这个功能相当的重要,运用面相当的广泛。举个例子在MVC的框架中,我们就是通过动态的加载程序集,然后遍历程序集里面所有继承了Controller的类型,最后在根据输入的URL与这些控制器的名称进行比较得到我们最终请求的控制器,知道了这个原理,那我们完全可以另起一个专门的类库项目,保存MVC控制器类型,使得程序结构更加的合理,如图:

 

我把MVC项目中的Controlls文件夹干掉了,新起了个项目存放我们MVC控制器。

反射在范型方面的运用

  范型分为两种类型,一种是开放型,一种是闭合型。举个例子List<T>就是个开放型的,因为我们的T类型还不确定;List<int>就是个闭合型的,范型的类型参数已经确定了,下面我来看一看反射在范型的具体运用

  1.范型的类型名称(公共运行库中的名称)

T = typeof(List<>);
Console.WriteLine(T);
T = typeof(List<int>);
Console.WriteLine(T);            

从图上可以看出,范型在公共运行库中的名称和我们想象中的如:System.Collections.Generic.List<int>这样有所差别的。所以当我们尝试typeof(System.Collections.Generic.List<int>)会报错的。接着我们来分析下上面两个类名,其中‘1代表范型类型参数的个数;Dictionary<,>带两个参数就会是’2;‘1后面的类型名则代表范型的类型参数的名称了。但是对于开放型范型,它是没太大的意义,仅仅表示一个占位符,所以我们尝试typeof(System.Collections.Generic.List`1)是不会有任何问题的,相当于typeof(List<T>)

  2.开放型范型和闭合型范型的相互转化。为什么要说这个,这是因为我们的开放型范型是没办法在程序中被实例化以及被调用的,真正到了运用的时候必须转化为闭合型范型的,如何转化如代码所示

Type t = typeof(List<>); //开放型
Console.WriteLine(t);
t = t.MakeGenericType(typeof(int));//闭合型
Console.WriteLine(t);
t = t.GetGenericTypeDefinition();//开放型
Console.WriteLine(t);

我们看到MakeGenericType()使我们的开放型范型转成了闭合型范型,T被int类型给替换了,而GetGenericTypeDefinition()则相反

3.创建范型实例。首先说明一点开放型范型是没办法实例化的,测试如下

 try
 {
      Type T = typeof(List<>);
      //T = T.MakeGenericType(typeof(int));
      List<int> obj = Activator.CreateInstance(T) as List<int>;
      Console.WriteLine("成功");
  }
 catch
 {
     Console.WriteLine("失败");
 }

而当我们把注释去掉,把开放型范型转化为闭合型,我们能成功创建范型实例。

4.范型的非范型方法调用。首先看实例方法,例如List<int>的Add方法,有两种途径:

Type T = typeof(List<>);
T = T.MakeGenericType(typeof(int));
List<int> obj = Activator.CreateInstance(T) as List<int>;
MethodInfo methodInfo= T.GetMethod("Add");
methodInfo.Invoke(obj,new object[]{1});

Type T = typeof(List<>);
T = T.MakeGenericType(typeof(int));
List<int> obj = Activator.CreateInstance(T) as List<int>;
obj.Add(1);

在看一看范型的静态方法的调用,我们新建一个RefTest<T>类,然后调用它的静态方法SayHello

 public class RefTest<T>
    {
        public static T TestMethod<M>(M pars)
        {
            return default(T);
        }
        public static string SayHello()
        {
            return "hello";
        }
  }

Type T = typeof(RefTest<>);
T = T.MakeGenericType(typeof(string));
MethodInfo methodInfo = T.GetMethod("SayHello");
methodInfo.Invoke(null, new object[] {});
Console.WriteLine("成功");

5.范型的范型方法调用。要注意两点,第一我们的范型必须是闭合型的范型;第二点在调用范型方法的时候一定要为方法指定具体的类型,调用         MakeGenericMethod方法指定该范型方法的范型参数的具体类型。

Type T = typeof(RefTest<>);
T = T.MakeGenericType(typeof(int));
MethodInfo methodInfo = T.GetMethod("TestMethod");
//特别注意MakeGenericMethod方法将返回闭合型的方法,只有闭合型方法才能被调用
methodInfo = methodInfo.MakeGenericMethod(typeof(string));
methodInfo.Invoke(null, new object[] { "hello" });

总结

本文强调了反射动态加载创建实体的重要性,并给出了反射动态加载程序集的一般规律,同时分享了反射在范型方面的一些运用,希望给大家带来帮助。

反射的一些探索,布布扣,bubuko.com

时间: 2024-10-06 21:35:19

反射的一些探索的相关文章

探索Java反射机制

Java反射机制,可以说我们平常开发中用的很多,尽管作为开发人员,我们并没有什么感觉,那是因为框架的设计者,已经为我们封装好了,我们只需要用去调用一下就可以了.以前也用到过反射,包括自己也写过,也学到过,但是我感觉都比较浅,今天有时间,我觉得有必要去慢慢的深入了解一下. 我们把能够分析类能力的程序称为反射(reflective).反射机制功能及其强大,简单说几个反射的用途: 1.在运行中分析类的能力: 2.在运行中查看对象,例如编写一个通用的tostring方法: 3.实现通用的数组操作代码:

Android开发艺术探索——第七章:Android动画深入分析

Android开发艺术探索--第七章:Android动画深入分析 Android的动画可以分成三种,view动画,帧动画,还有属性动画,其实帧动画也是属于view动画的一种,,只不过他和传统的平移之类的动画不太一样的是表现形式上有点不一样,view动画是通过对场景的不断图像交换而产生的动画效果,而帧动画就是播放一大段图片,很显然,图片多了会OOM,属性动画通过动态的改变对象的属性达到动画效果,也是api11的新特性,在低版本无法使用属性动画,但是我们依旧有一些兼容库,OK,我们还是继续来看下详细

Java 热部署深入探索

简介 在 Java 开发领域,热部署一直是一个难以解决的问题,目前的 Java 虚拟机只能实现方法体的修改热部署,对于整个类的结构修改,仍然需要重启虚拟机,对类重新加载才能完成更新操作.对于某些大型的应用来说,每次的重启都需 要花费大量的时间成本.虽然 osgi 架构的出现,让模块重启成为可能,但是如果模块之间有调用关系的话,这样的操作依然会让应用出现短暂的功能性休克.本文将探索如何在不破坏 Java 虚拟机现有行为的前提下,实现某个单一类的热部署,让系统无需重启就完成某个类的更新. 类加载的探

深入探索 Java 热部署

在 Java 开发领域,热部署一直是一个难以解决的问题,目前的 Java 虚拟机只能实现方法体的修改热部署,对于整个类的结构修改,仍然需要重启虚拟机,对类重新加载才能完成更新操作.对于某些大型的应用来说,每次的重启都需要花费大量的时间成本.虽然 osgi 架构的出现,让模块重启成为可能,但是如果模块之间有调用关系的话,这样的操作依然会让应用出现短暂的功能性休克.本文将探索如何在不破坏 Java 虚拟机现有行为的前提下,实现某个单一类的热部署,让系统无需重启就完成某个类的更新. 类加载的探索 首先

ASP.Net请求处理机制初步探索之旅 - Part 2 核心

  上一篇我们了解了一个请求从客户端发出到服务端接收并转到ASP.Net处理入口的过程,这篇我们开始探索ASP.Net的核心处理部分,借助强大的反编译工具,我们会看到几个熟悉又陌生的名词(类):HttpWorkerRequest.HttpRuntime.HttpContext.HttpApplication等. 一.第一个入口:ISAPIRuntme.ProcessRequest()    ISAPIRuntime是进入NET托管环境的入口,在其PR方法中通过一个ecb句柄指向了当前请求报文体的

容器框架的探索思绪日记

1.开发框架感受: 对于框架的思考前段时间,收到做框架的任务,很是激动,一种很高大上的研究任务,感觉只要自己开发出来,就上了一个档次.乐此不疲的研究起来,一段时间之后发现无从下手,凭着内心对这个面向对象的喜欢,又卷土重来了一次,直到现在,做出了雏形,感觉很欣慰. 说说这短时间的感受:感觉对框架的理解突然就强大了很多,以前就是一个十足的程序员,现在有框架的意思了.随着对面向对象的不断深化,就很想做这个框架,这个动力一直牵引着我往前走,把JAVA学习的进度给放慢了.雏形出来了,我觉得我的选择是对的,

一种实现C++反射功能的想法(一)

Java的反射机制很酷, 只需知道类的名字就能够加载调用. 这个功能很实用, 想象一下, 用户只需指定类的名称, 就可以动态绑定类型, 而且只需通过字符串指定, 字符串的使用可以使得用户的修改只需修改一个配置文件就行, 仅仅修改配置文件, 连重新编译都不需要. 喔噢噢, 这种做法将代码的解耦程度做到了极致, 这种技术也不是什么新技术, spring, struct, hibernate......大多数框架都使用到java的反射机制, 而且是作为核心技术, 如果你还未了解反射的话, 赶紧抓紧时间

.Net事件探索

[原].Net事件探索 对自定义事件的疑惑 我创建了一个 "EventTest.Book" 类,包含一个 "Name" 属性和一个 "NameChanged" 事件.在类外部判断 "NameChanged" 事件的委托是否为空时,编辑器提示“错误: 事件 "EventTest.BookNameChanged" 只能出现在 += 或 -= 的左边(从类型 "EventTest.Book"

黑马程序员——反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类:在运行时构造任意一个类的对象:在运行时判断任意一个类所具有的成员变量和方法:在运行时调用任意一个对象的方法:生成动态代理. 有时候我们说某个语言具有很强的动态性,有时候我们会区分动态和静态的不同技术与作法.我们朗朗上口