使用C#的AssemblyResolve事件和TypeResolve事件动态解析加载失败的程序集
我们知道反射是 依赖注入 模式的基础,依赖注入要求只在项目中引用定义接口的程序集,而不引用接口实现类的程序集,因为接口实现类的程序集应该是通过反射来动态加载的,这样才能保证接口与其实现类之间的松耦合。可是有时候我们使用反射动态加载程序集的时候会失败,因为除非我们手动将接口实现类的程序集放在项目生成后的bin目录下,或者是在GAC中,否者.Net Framework/.Net Core并不知道该到哪里去寻找接口实现类的dll程序集文件。幸运的是我们如果使用 AppDomain.CurrentDomain.AssemblyResolve事件和AppDomain.CurrentDomain.TypeResolve事件,就可以通过C#代码来自定义程序集加载逻辑,当C#反射解析程序集或类型失败的时候,通过执行自定义程序集加载逻辑来找到相应的程序集dll文件。
例如现在我们定义了一个普通的C#类库项目叫MessageDisplay,如下图所示
里面只包含了一个C#类MessageDisplayHelper.cs文件,MessageDisplayHelper.cs代码如下:
using System; namespace MessageDisplay
{
public class MessageDisplayHelper
{
public string Display()
{
return "This is a message!";
}
}
}
然后我们定义一个C#控制台程序叫AssemblyResolverConsle:
在这个控制台程序中我们不直接引用MessageDisplay程序集,而是使用反射加载程序集MessageDisplay,然后使用反射动态构造MessageDisplayHelper类。由于我们没有在控制台程序AssemblyResolverConsle中直接引用MessageDisplay程序集,所以在调用Assembly.Load方法动态加载程序集的时候会失败,从而触发AppDomain.CurrentDomain.AssemblyResolve事件,而之后在调用Type.GetType("MessageDisplay.MessageDisplayHelper")时也会失败,又触发AppDomain.CurrentDomain.TypeResolve事件。
AssemblyResolverConsle控制台程序的Program.cs文件代码如下:
using System;
using System.Reflection; namespace AssemblyResolverConsle
{
class Program
{
static void Main(string[] args)
{
//当程序集(Assembly)通过反射加载失败的时候会触发AssemblyResolve事件,这里注册AssemblyResolve事件的处理函数为CurrentDomain_AssemblyResolve
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
//当类型(Type)通过反射加载失败的时候会触发TypeResolve事件,这里注册TypeResolve事件的处理函数为CurrentDomain_TypeResolve
AppDomain.CurrentDomain.TypeResolve += CurrentDomain_TypeResolve; //这里通过调用Assembly.Load方法反射加载MessageDisplay程序集会失败,因为本项目中没有引用该程序集,而且MessageDisplay程序集的dll文件也不在本项目生成的bin目录下,也不在GAC中。所以这里会触发AssemblyResolve事件,调用处理函数CurrentDomain_AssemblyResolve来尝试执行自定义程序集加载逻辑,然后处理函数CurrentDomain_AssemblyResolve会为这里的Assembly.Load方法返回MessageDisplay.dll程序集
var messageDisplayAssembly = Assembly.Load("MessageDisplay");
//使用反射动态调用MessageDisplayHelper类的构造函数
var messageDisplayHelper = messageDisplayAssembly.CreateInstance("MessageDisplay.MessageDisplayHelper");
Console.WriteLine(messageDisplayHelper.ToString()); //同样这里通过Type.GetType方法反射加载MessageDisplay程序集也会失败,会触发AssemblyResolve事件,调用处理函数CurrentDomain_AssemblyResolve来尝试执行自定义程序集加载逻辑,然后处理函数CurrentDomain_AssemblyResolve会为这里的Type.GetType方法返回所需要的程序集MessageDisplay.dll
//和Assembly.Load方法不同,如果AssemblyResolve事件的处理函数CurrentDomain_AssemblyResolve为Type.GetType方法返回了null,Type.GetType方法并不会抛出异常,而是也返回一个null
Type type = Type.GetType("MessageDisplay.MessageDisplayHelper, MessageDisplay");
Console.WriteLine(type.ToString()); //下面这里通过Type.GetType方法只反射类型MessageDisplay.MessageDisplayHelper,而不反射程序集MessageDisplay,所以会触发TypeResolve事件,调用处理函数CurrentDomain_TypeResolve来尝试执行自定义程序集加载逻辑,然后处理函数CurrentDomain_TypeResolve会为这里的Type.GetType方法返回所需要的程序集MessageDisplay.dll
//同样如果TypeResolve事件的处理函数CurrentDomain_TypeResolve为Type.GetType方法返回了null,Type.GetType方法并不会抛出异常,而是也返回一个null
type = Type.GetType("MessageDisplay.MessageDisplayHelper");
Console.WriteLine(type.ToString()); Console.WriteLine("Press any key to quit...");
Console.ReadLine();
} /// <summary>
/// TypeResolve事件的处理函数,该函数用来自定义程序集加载逻辑
/// </summary>
/// <param name="sender">事件引发源</param>
/// <param name="args">事件参数,从该参数中可以获取加载失败的类型的名称</param>
/// <returns></returns>
private static Assembly CurrentDomain_TypeResolve(object sender, ResolveEventArgs args)
{
//根据加载失败类型的名字找到其所属程序集并返回
if (args.Name.Split(",")[] == "MessageDisplay.MessageDisplayHelper")
{
//我们自定义的程序集加载逻辑知道MessageDisplay.MessageDisplayHelper类属于MessageDisplay程序集,而MessageDisplay程序集在C:\AssemblyResolverConsle\Reference\MessageDisplay.dll这个路径下,所以这里加载这个路径下的dll文件作为TypeResolve事件处理函数的返回值
return Assembly.LoadFile(@"C:\AssemblyResolverConsle\Reference\MessageDisplay.dll");
} //如果TypeResolve事件的处理函数返回null,说明TypeResolve事件的处理函数也不知道加载失败的类型属于哪个程序集
return null;
} /// <summary>
/// AssemblyResolve事件的处理函数,该函数用来自定义程序集加载逻辑
/// </summary>
/// <param name="sender">事件引发源</param>
/// <param name="args">事件参数,从该参数中可以获取加载失败的程序集的名称</param>
/// <returns></returns>
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
//根据加载失败程序集的名字找到该程序集并返回
if (args.Name.Split(",")[] == "MessageDisplay")
{
//我们自定义的程序集加载逻辑知道MessageDisplay程序集在C:\AssemblyResolverConsle\Reference\MessageDisplay.dll这个路径下,所以这里加载这个路径下的dll文件作为AssemblyResolve事件处理函数的返回值
return Assembly.LoadFile(@"C:\AssemblyResolverConsle\Reference\MessageDisplay.dll");
} //如果AssemblyResolve事件的处理函数返回null,说明AssemblyResolve事件的处理函数也无法找到加载失败的程序集,那么整个程序就会抛出异常报错
return null;
}
}
}
所以AppDomain.CurrentDomain.AssemblyResolve事件和AppDomain.CurrentDomain.TypeResolve事件,给反射加载程序集失败和加载类型失败提供了一个很好的解决途径,可以允许开发者自定义程序集解析逻辑。我们不再需要把一个.Net程序所需要用到的所有dll文件都要求放到bin目录下或GAC中,而是可以放在任何位置,通过AppDomain.CurrentDomain.AssemblyResolve事件和AppDomain.CurrentDomain.TypeResolve事件的处理函数来动态加载。
使用C#的AssemblyResolve事件和TypeResolve事件动态解析加载失败的程序集的更多相关文章
- 使用C#的AssemblyResolve事件动态解析加载失败的程序集
我们知道反射是 依赖注入 模式的基础,依赖注入要求只在项目中引用定义接口的程序集,而不引用接口实现类的程序集,因为接口实现类的程序集应该是通过反射来动态加载的,这样才能保证接口与其实现类之间的松耦合. ...
- 深入理解DOM事件类型系列第六篇——加载事件
前面的话 提到加载事件,可能想到了window.onload,但实际上,加载事件是一大类事件,本文将详细介绍加载事件 load load事件是最常用的一个事件,当页面完全加载后(包括所有图像.java ...
- JavaScript-onerror事件:图片加载失败后不显示
HTML: <img src="http://www.mazey.net/images/upload/image/20170518/1495122198180663.gif" ...
- jQuery 滚动条 滚动到底部(下拉到底部) 加载数据(触发事件、处理逻辑)、分页加载数据
1.针对浏览器整个窗口滚动 主要代码: <script type="text/javascript"> ; function GetProductListPageFun ...
- 使用事件捕获实时捕获img是否加载完毕, 实现iframe内容高度自动适应
如何判断在html中图片加载完毕呢? 给img图片加onload事件呗. 如何判断一个界面中所有的图片加载完毕呢? 给所有的图片加上onload事件呗. 如果有1000张图片那要怎么绑定事件呢? 我们 ...
- 利用伪类选择器与better-scroll的on事件所完成的上拉加载
之前给大家分享过一篇上拉加载 利用了better-scroll的pullUpDown 和DOM元素的删除添加 感觉那样不太好 今天给大家分享一个不同的上拉加载思想 代码如下 class List { ...
- setTimeout用于取消多次执行mouseover或者mouseenter事件,间接实现hover的悬停加载的效果.
Mouseenter在鼠标滑上去不会对其子元素也发生监听, Mouseover在鼠标滑上去会对其子元素发生监听. 所以对于事件的监听,我们要看需求,这里是对父元素的监听,不需要对子元素做监听.就用mo ...
- 监听table滚动事件,滚动到底部时加载数据
mounted() { this.$refs.scrollTable.addEventListener( 'scroll',(event) => { this.getDistance(event ...
- 重温.NET下Assembly的加载过程 ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线
重温.NET下Assembly的加载过程 最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后 ...
随机推荐
- The method setItems(String) in the type ForTokensTag is not applicable for the arguments (Object)
1. 问题 看到这个错误以为是貌似jsp页面有误,c:forTokens标签用错了?? An error occurred at line: in the jsp file: /WEB-INF/pag ...
- Scrum过程管理学习心得
认识敏捷开发 在课堂上了解了瀑布开发,又在课下学习敏捷开发过程后,我发现,敏姐团队做的开发工作虽然和瀑布开发一模一样,但他们的做事方式很不一样.简单来说,两者的差别在于:瀑布开发必须先完成当前的步骤后 ...
- 动态注册broadcast的安全考虑
一.android service通知activity更新方式有1. service 通过广播的形式发送broadcast,向这个activity的内部类发广播的消息来更新界面2. service直接 ...
- Gitlab命令行简单使用
使用Gitlab拉取远程文件到本地,然后再创建新分支的流程: git clone url #将远程分支拉取到本地: git status / git branch #查看git的状态和分支情况,g ...
- Pycharm使用中背景颜色和更改项目的Python版本
一.背景颜色 颜色是每一个人都会去更改的,而且可以保护眼睛! 第二步: 选择图中画框的位置,便可以更改背景颜色! 二.项目版本的更改: python2 和 python3 有很大的不同,使用pytho ...
- mongodb 启动
>mongod.exe --dbpath C:\Environ\mongodb-3.0.6\data\db >mongod.exe --logpath "C:\Environ\ ...
- 【Z】段错误Segment Fault定位,即core dump文件与gdb定位
使用C++开发系统有时会出现段错误,即Segment Fault.此类错误程序直接崩溃,通常没有任何有用信息输出,很难定位bug,因而无从解决问题.今天我们介绍core dump文件,并使用gdb进行 ...
- July 09th 2017 Week 28th Sunday
He that boasts of his own knowledge proclaims ignorance. 夸耀知识实乃无知. Honestly speaking, I don't agree ...
- 【转】[C++]实现委托模型
原文地址:http://www.cnblogs.com/zplutor/archive/2011/09/17/2179756.html 我对.Net的委托模型印象很深刻,使用委托,可以快速实现观察者模 ...
- 将POST请求转换为DELETE、PUT等请求的方法
一.在WEB工程的web.xml文件中配置HiddenHttpMethodFilter 二.form 表单中添加一个隐藏域 name="_method" value="D ...