目录
一、简介
不久之前我写过一篇 Winform 自动更新的帖子,虽然很方便,但也有个问题,那就是如果程序运行中出现了错误,那么必须重启客户端,才能更新到最新的版本,如果程序正在运行中,是无法动态的去改变原有的逻辑的。
有人可能会说,这有什么难的,我自己写个 dll,将这个 dll 添加到项目的引用中,运行程序后,把 dll 给替换掉,逻辑不就变了嘛?这个方案没毛病,但它也有个问题,程序运行后,这时 dll 已经被占用了,没办法替换了,如下图:
后面,我在 github 发现了一个开源框架 ECSharp ,这个框架是有热更新的功能的,于是我试了一下,发现确实可以通过替换dll方式来实现热更新,而且在程序运行过程中,随便怎么替换 dll,都不会有文件占用问题,下面就根据官方的一些简介:
ECSharp (原:EasySharpFrame)
github 地址:https://github.com/suxf/ECSharp
功能:
1.HTTP服务
2.Websocket服务
3.HyperSocket<自定义Socket服务>
4.TimeFlow<时间流>
5.Sqlserver数据库助手
6.Mysql数据助手
7.Redis数据库助手
8.Log功能
9.热更新功能
10.可变变量
11.事件与命令
二、 ECSharp热更新演示
首先,在 Github 上把 ECSharp 源码下载到本地,因为我在项目中,是直接复制官方的源码,或者,你也可以在 NuGet 上安装 EasySharpFrame 插件也是一样的。
新建一个 .NET6 控制台项目,取名:HotfixTest,将 ECSharp 部分源码复制过来,添加到项目中,如下图:
上图的代码中,Player.cs 是用来测试的,Program.cs 是创建项目时默认生成的。
下面的代码有些混乱,这只是Demo,当时也是用来测试功能的
Player.cs 代码如下:
using ECSharp.Hotfix;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HotfixTest
{
public class Player: AgentData
{
public string Name { get; set; } = "张三";
public void Say()
{
Console.WriteLine("我是角色:" + Name);
}
}
public abstract class Dog : AgentData
{
public string Names = "恶狗";
public int Age = 3;
}
/// <summary>
/// 野狗
/// </summary>
public class WildDog : Dog
{
public int Height = 10;
}
/// <summary>
/// 鬣狗
/// </summary>
public class Hyena : Dog
{
public int Height = 2;
}
}
Program.cs
using ECSharp.Hotfix;
namespace HotfixTest
{
internal class Program
{
static void Main(string[] args)
{
Player player = new Player();
player.Name = "李四";
player.Say();
WildDog dog1 = new WildDog();
Hyena dog2 = new Hyena();
Console.WriteLine("=============================");
Console.WriteLine("dog1.Names:" + dog1.Names);
Console.WriteLine("dog1.Age:" + dog1.Age);
Console.WriteLine("dog1.Height:" + dog1.Height);
Console.WriteLine("=================");
Console.WriteLine("dog2.Height:" + dog2.Height);
Console.WriteLine("=============================");
HotfixMgr.Load("Hotfixs", "Hotfixs.Main", new string[] { "参数1", "参数2" }, "Hello");
}
}
}
HotfixMgr.Load 方法就是用来加载热更的 dll,下面我来解释下这几个参数的意思:
1)Hotfixs --- dll 的文件名,必须保持一致,否则读取不到 dll。
2)Hotfixs.Main --- 要调用的类名,以 “命名空间.类名” 表示。
3)new string[] { "参数1", "参数2" } --- 这里是要调用的方法的参数。
4)Hello --- 方法的名字, 上面的 new string[] { "参数1", "参数2" } 就是给当前方法传递的参数。
下面我们来写热更新的 dll 。
新建一个基于 .NET6 的 类库,取名:Hotfixs,添加一个脚本 Main.cs,把上面的 HotfixTest 生成的 dll 添加到当前项目中,添加 dll 直接用 HotfixTest 项目 Debug 目录中的 HotfixTest.dll 就好了,如下图:
Main.cs
using ECSharp.Hotfix;
using HotfixTest;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hotfixs
{
public class Main
{
public static void Hello(string[] args)
{
Console.WriteLine("=============================");
if (args.Length > 0)
{
for (int i = 0; i < args.Length; i++)
{
Console.WriteLine(args[i]);
}
}
Console.WriteLine("这是热重载dll,啊哈哈哈哈哈");
Player player1 = AgentDataPivot.AddOrGetObject<Player>("player1");
player1.Say();
player1.Name = "李四他爸";
player1.Say();
Dog dog1 = new WildDog();
Dog dog2 = new Hyena();
Console.WriteLine("=============================");
AbsWildDog absWildDog = dog1.GetAbstractAgent<AbsWildDog>();
absWildDog.self.Names = "抽象野狗";
absWildDog.self.Age = 4;
absWildDog.Test();
Console.WriteLine("=============================");
AbsHyena absHyena = dog2.GetAbstractAgent<AbsHyena>();
absHyena.self.Names = "抽象鬣狗";
absHyena.self.Age = 6;
absHyena.Test();
Console.WriteLine("=============================");
}
}
public class AbsWildDog : AbstractAgent, IAgent<WildDog>
{
public WildDog self => _self as WildDog;
public void Test()
{
Console.WriteLine("AbsWildDog-Names:" + self?.Names);
Console.WriteLine("AbsWildDog-Age:" + self?.Age);
Console.WriteLine("AbsWildDog-Height:" + self?.Height);
}
protected override void Initialize()
{
Console.WriteLine("AbsWildDog-Initialize方法");
}
}
public class AbsHyena : AbstractAgent, IAgent<Hyena>
{
public Hyena self => _self as Hyena;
public void Test()
{
Console.WriteLine("AbsHyena-Names:" + self?.Names);
Console.WriteLine("AbsHyena-Age:" + self?.Age);
Console.WriteLine("AbsHyena-Height:" + self?.Height);
}
protected override void Initialize()
{
Console.WriteLine("AbsHyena-Initialize方法");
}
}
}
将 Hotfixs 项目生成的 Hotfixs.dll 复制到 HotfixTest 项目中的 Debug 目录下,
运行 HotfixTest 项目,就可以得到下面的输出:
这个框架的逻辑可能稍微有点复杂,多看看就能理解了,下面就开始 Winform 热更功能的实战。
三、Winform 热更新实战
新建一个基于 .Net6 的 Winform 项目,取名:HotfixWinForms,将上面 HotfixTest 项目中的三个文件夹复制过来
将程序设置为 控制台输出
然后在 Form1 界面中,添加四个按钮,
新建一个脚本 PrintBase.cs
namespace HotfixWinForms
{
public abstract class PrintBase
{
public abstract void Say1();
public abstract void Say2();
public abstract void Say3();
}
}
接着新建脚本 Print.cs
namespace HotfixWinForms
{
public class Print : PrintBase
{
public override void Say1()
{
Console.WriteLine("喜欢唱,跳,rap,篮球");
}
public override void Say2()
{
Console.WriteLine("你干嘛哈哈哎呦");
}
public override void Say3()
{
Console.WriteLine("哎呦哈哈~~");
}
}
}
接着新建脚本 HotfixBLL.cs ,在 Form1 类中只要调用 PrintBase 的抽象方法即可,具体的内容由子类 Print 去实现,这就是 C# 中多态的常见用法。
namespace HotfixWinForms
{
public class HotfixBLL
{
public static PrintBase PrintBases = new Print();
}
}
接下来就给 Form1 的四个按钮,添加点击事件。
using ECSharp.Hotfix;
namespace HotfixWinForms
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
HotfixBLL.PrintBases.Say1();
}
private void button2_Click(object sender, EventArgs e)
{
HotfixBLL.PrintBases.Say2();
}
private void button3_Click(object sender, EventArgs e)
{
HotfixBLL.PrintBases.Say3();
}
private void button4_Click(object sender, EventArgs e)
{
bool result = HotfixMgr.Load("Hotfix1", "Hotfix1.Main", null, "Hotfix");
Console.WriteLine("加载热重载结果:" + result);
}
}
}
此时,我们运行程序,分别点击按钮1,2,3,输出:
最后一个热更新按钮,先不用管,由于 dll 还没放进来,此时点击会报错,接下来我们就来完成热更新部分。
新建一个基于 .Net6 的 类库 项目,取名:Hotfix1。
将 HotfixWinForms 项目中 Debug 文件夹中的 HotfixWinForms.dll 添加到当前的项目引用。
添加一个脚本 Main.cs
using HotfixWinForms;
namespace Hotfix1
{
public class Main
{
public static void Hotfix(string[] args)
{
HotfixBLL.PrintBases = new HotfixPrint();
Console.WriteLine("重写PrintBases");
}
}
public class HotfixPrint : PrintBase
{
public override void Say1()
{
Console.WriteLine("喜欢,唱,跳,rap,篮球,music,鸡你太美~");
}
public override void Say2()
{
Console.WriteLine("你干嘛~哈哈~哎呦~");
}
public override void Say3()
{
Console.WriteLine("哎呦~啊哈哈~maige");
}
}
}
写完代码后,点击生成,将生成的 Hotfix1.dll 文件复制到 HotfixWinForms 项目中的 Debug 目录中,那么当前的工作就完成了,接下来就是测试了。
运行 HotfixWinForms 项目,分别的按钮 1、2、3,可以看到,打印和之前一样的,再点击热更新按钮,控制台输出了 “加载热重载结果:True”,说明热更 dll 已经加载成功了,再次分别点击按钮1、2、3,就会发现,打印现在输出的是 Hotfix1 项目中 HotfixPrint 类的打印文字,这就是说明代码成功的实现了热更新,并且,程序也不用关闭。
此时,Hotfix1.dll 载入就相当于给程序打了一个补丁,是可以改变原有逻辑的,而且,在程序运行过程中还可以继续替换,效果和之前一样的。
另外,如果有兴趣可以使用下面的 NLua 热更新,这个帖子有一个完整的Demo,虽然没写的那么好,但也是展现了一个热更基本的流程,而且功能更加强大,也不用去打DLL补丁那么繁琐,只是还没测试他的稳定性,有用在工作中的可以留言说下感受。
源码: 点击下载
结束
如果这个帖子对你有用,欢迎 关注 + 点赞 + 留言,谢谢
end