有的时候我们想更新我们正在执行中的代码,而不想软件重启。
微软提供的标准方法是通过应用程序域来实现代码热更新,意思就是说,把自己想要进行热更新的代码放到另外一个应用程序域中,在检测到代码需要变更的时候,卸载掉那个程序域然后重新加载来实现代码热更新。按照微软的说法,一个应用程序域是无法实现代码热更新的。
但是,一下方法确实是可以在单个应用程序域中实现代码热更新的,本人尝试过,在公司的上班中,软件的频繁重启让人很烦,又不能在另外一个程序域中执行,所以尝试了一下办法,在一个应用程序域中改变代码是可行的。
方法如下:假设程序集A中有类a,程序集B中有类b,b要去调用a的方法,这个情况下可以对a的部分代码(仅对方法和属性有效)进行热更新。第一步是针对类a 进行代码重写,生成一个假的A.dll,但是具有和包含真A中类,字段,属性,和方法签名,把真A的Dll放到另外一个文件夹。当B去调用A中方法的时候,假的A会通过字节数组的形式将真A保存在自己的一个Assembly(最好放在一个其他类的静态变量中,我是那么做的)变量中,并且检测真A的文件改动,当真A发生变化的时候对Assembly变量通过字节数组的形式重新赋值。这个样子就可以就可以实现部分代码热更新。
假A对真A代码的重写原则:假a类中至少要有三个变量,一个object变量,用来存对真a对象的引用。一个Type变量,用来存储真a的类型信息,还有真A的Assembly变量。假a在初始化的时候,要初始化刚才的那三个变量,对假a方法的调用通过反射转换为对真a对象(object变量,用来存对真a对象的引用)的调用。并且把真a方法的返回结果返回回去在真A变化之后,假A重新加载真A,这个时间点之后,在被创建的假a执行的代码已经是代码变化之后真A的代码,就可以实现代码特更新。
把真A代码重构并且生成编译出假A是一个很费劲容易出错的过程,如下是我生成假A的代码逻辑,应该是存在某些问题的(嘿嘿,主要是说明方法)
using Microsoft.CSharp;
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace DllLoad
{
class Core2
{
List<string> dlls = new List<string>();
string outpath = string.Empty;
public EventHandler CompileCompleted;
public Core2()
{
}
public void SetInfo(List<string> dlls, string outpath)
{
this.dlls = dlls;
this.outpath = outpath;
}
public void StartCompile()
{
try
{
dlls.ForEach(o =>
{
Assembly assem = Assembly.LoadFrom(o);
Compile(assem, outpath);
});
}
catch (Exception e)
{
KeyValuePair<bool, string> error = new KeyValuePair<bool, string>(false, e.Message);
CompileCompleted(error, null);
return;
}
KeyValuePair<bool, strin