C#?NLua?Winform實(shí)現(xiàn)熱更新的項(xiàng)目實(shí)踐
一、概述
NLua 是一個(gè)用于 .NET 平臺(tái)的 Lua 腳本綁定庫。它允許在 C# 代碼中嵌入 Lua 腳本,并允許兩者之間進(jìn)行交互。NLua 的主要特點(diǎn)包括:
- 輕量級(jí):NLua 是一個(gè)輕量級(jí)的庫,易于集成到現(xiàn)有的 .NET 項(xiàng)目中。
- 動(dòng)態(tài)類型:Lua 是動(dòng)態(tài)類型的語言,這意味著變量的類型可以在運(yùn)行時(shí)改變。
- 靈活的綁定:NLua 提供了靈活的綁定機(jī)制,使得 C# 和 Lua 之間的數(shù)據(jù)交互變得簡(jiǎn)單。
- 豐富的 API:NLua 提供了豐富的 API,以便在 Lua 腳本中調(diào)用 .NET 的類和方法。
- 調(diào)試支持:NLua 支持調(diào)試功能,方便開發(fā)者在 Lua 腳本中設(shè)置斷點(diǎn)、單步執(zhí)行等操作。
- 社區(qū)支持:NLua 有一個(gè)活躍的社區(qū),為開發(fā)者提供了一個(gè)交流和尋求幫助的平臺(tái)。
使用 NLua,你可以在 .NET 應(yīng)用中輕松地嵌入 Lua 腳本,從而實(shí)現(xiàn)動(dòng)態(tài)邏輯、配置管理、插件系統(tǒng)等功能。通過 NLua,你可以利用 Lua 的靈活性和易用性,同時(shí)保持 .NET 的強(qiáng)大功能和性能。
NLua 支持 UWP、Windows、Linux、Mac、iOS、Android 平臺(tái)。
NLua 源碼和示例 Github 地址
GitHub - NLua/NLua: Bridge between Lua and the .NET.
二、創(chuàng)建項(xiàng)目
創(chuàng)建一個(gè) .NET Framework Winform 項(xiàng)目,這里我用的版本是 4.8.1,取名叫 NLuaDemo,在 NuGet 中安裝 NLua 包。

winform 界面設(shè)計(jì)如下:

為了先看看 NLua 到底有沒有效果,先做一個(gè)小案例,讓你先熟悉一下 NLua 框架,在后面的案例中,會(huì)有完整的熱更新方式展示。
需求:用 Lua 腳本來改變 Winform 界面中的抽獎(jiǎng)人數(shù),數(shù)量隨意。
本章節(jié)非重點(diǎn),可以直接看第三章節(jié)
1.添加一個(gè)類 PublicData ,這個(gè)類用來保存公共數(shù)據(jù)(在第三節(jié)的案例中,PublicData 這個(gè)類將不再使用)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class PublicData
{
public static NLuaDemo.Form1 Form1s { get; set; }
}我將 From1 保存在這里,主要是為了方便后面 Lua 調(diào)用,在實(shí)際工作中,最好還是不要這么寫。
2.將 Form1.Designer.cs 中的控件設(shè)置為 public,以便讓 Lua 能直接調(diào)用 winform 控件。

3.將 Program 類改為如下,在第三節(jié)的案例中,這里還是會(huì)恢復(fù)默認(rèn)的代碼,這里只是演示,這并不是一個(gè)好的寫法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace NLuaDemo
{
internal static class Program
{
/// <summary>
/// 應(yīng)用程序的主入口點(diǎn)。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
PublicData.Form1s = new Form1();
Application.Run(PublicData.Form1s);
}
}
}
4.Form1 代碼如下(第三節(jié)案例中,代碼會(huì)不一樣)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using NLua;
namespace NLuaDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private Lua Luas = new Lua();
//lua 腳本的地址
private string LuaPath;
private void Form1_Load(object sender, EventArgs e)
{
Luas.State.Encoding = Encoding.UTF8;
LuaPath = $"{Application.StartupPath}\\main.lua";
LoadLua();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Luas.Close();
}
//熱重載
private void Button_HotUpdate_Click(object sender, EventArgs e)
{
Luas.DoString("update()");
}
private void LoadLua()
{
if (File.Exists(LuaPath))
{
Luas.LoadCLRPackage();
Luas.DoFile(LuaPath);
Luas["form1s"] = PublicData.Form1s;
}
}
}
}
5.在項(xiàng)目的 Debug 目錄中,新建一個(gè) main.lua 文件,加入下面代碼
form1s = {};
function update()
form1s.Label_RaffleNumber.Text = "抽獎(jiǎng)人數(shù):100" ;
end完成了上面的工作,現(xiàn)在可以開始測(cè)試了

可以看到,Lua 腳本確實(shí)改變了 winform 界面的數(shù)據(jù)。
這里為什么不把邏輯直接寫在 main.lua 中呢?
form1s = {};
form1s.Label_RaffleNumber.Text = "抽獎(jiǎng)人數(shù):100" ;
function update()
form1s.Label_RaffleNumber.Text = "抽獎(jiǎng)人數(shù):100" ;
end因?yàn)檫\(yùn)行后就會(huì)報(bào)錯(cuò):

由于 Lua 腳本在運(yùn)行后,會(huì)執(zhí)行所有的代碼,但是我們定義的 Lua 全局變量此時(shí)還沒有賦值,直接運(yùn)行當(dāng)然報(bào)錯(cuò)了,于是我將熱更新內(nèi)容寫入到 lua 腳本中的 update 方法中,等 C# 這邊初始化完成后,調(diào)用 update 方法就不會(huì)有問題了。
另外還有一種方法,使用 LoadFile 方法,我試了一下,沒有效果,可能是我用法不對(duì)
LuaFunction luaFunction = Luas.LoadFile("C:\\test.lua");有興趣的朋友可以去試試,有好的建議歡迎留言評(píng)論。
三、實(shí)現(xiàn) Lua 熱更新
在上面的案例中,我們是把 Form1 賦值給了 lua 腳本中的 form1s 這個(gè)變量(也可以叫表單),其實(shí)還可以這么寫:
private void LoadLua()
{
if (File.Exists(LuaPath))
{
Luas.LoadCLRPackage();
Luas.DoFile(LuaPath);
Luas["this"] = this;
}
}但是這種用法在 Lua 腳本用會(huì)有錯(cuò)誤,在 Visual Studio Code 中搭建好 Lua 開發(fā)環(huán)境,就會(huì)看到提示:未定義的全局變量 “this”

這個(gè)不用管它,lua 腳本只要在 C# 項(xiàng)目中運(yùn)行不報(bào)錯(cuò)就行了。
這里的寫法改變后,上一個(gè) demo 中代碼也要改改了。
1.Program 類恢復(fù)默認(rèn)的寫法:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace NLuaDemo
{
internal static class Program
{
/// <summary>
/// 應(yīng)用程序的主入口點(diǎn)。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//PublicData.Form1s = new Form1();
//Application.Run(PublicData.Form1s);
Application.Run(new Form1());
}
}
}2.PublicData 類可以刪掉,或者注釋所有的代碼,現(xiàn)在用不上了。
另一個(gè),Lua 調(diào)用 C# 方法,要用英文的冒號(hào)去調(diào)用方法,而不是像 C# 一樣用點(diǎn)去調(diào)用,這個(gè)在 Unity3d 中 XLua 等熱更新框架是同樣的用法,具體用法可以參考下面 main.lua 代碼。
--import("System")
--import('System.Windows.Forms')
--import("System.Threading");
--form1s = {};
--獎(jiǎng)品
prize = {"手機(jī)", "電腦", "耳機(jī)", "鼠標(biāo)", "鍵盤", "充電寶"}
--抽獎(jiǎng)人
participant = {"張三", "李四", "老王", "狗蛋", "鐵剩"}
--熱重載
function update()
this:Print("抽獎(jiǎng)人 長(zhǎng)度是:" .. #participant)
this.Label_RaffleNumber.Text = "抽獎(jiǎng)人數(shù):" .. #participant
this.Label_PrizesNum.Text = "獎(jiǎng)品數(shù):" .. #prize
--獎(jiǎng)品下拉框
this.ComboBox_Prize.Items:Clear()
for i,item in ipairs(prize) do
this.ComboBox_Prize.Items:Add(item)
end
this.ComboBox_Prize.SelectedIndex = 0
--抽獎(jiǎng)人下拉框
this.ComboBox_LotteryGiver.Items:Clear();
for i,item in ipairs(participant) do
this.ComboBox_LotteryGiver.Items:Add(item);
end
this.ComboBox_LotteryGiver.SelectedIndex = 0
end
--抽獎(jiǎng)按鈕
function raffle()
--隨機(jī)獎(jiǎng)品
local prizeIndex = math.random(1, #prize)
local prize = prize[prizeIndex]
--隨機(jī)抽獎(jiǎng)人
local participantIndex = math.random(1, #participant)
local participant = participant[participantIndex]
local content = "抽獎(jiǎng)人:"..participant..",獲得獎(jiǎng)品:"..prize
this:Print(content)
end注意上面 lua 腳本中的 Label_RaffleNumber,Label_PrizesNum,ComboBox_Prize 等關(guān)鍵字,這都是 winform 的控件,如果你用的不是我的源碼,winform 控件名和 lua 腳本中的也不一致,那么運(yùn)行就會(huì)報(bào)錯(cuò):
錯(cuò)誤:

當(dāng)前 demo 所有的控件名

winform 這個(gè)控制臺(tái)不知道怎么回事,lua 中打印用的 print 方法打印出來全是亂碼,但是 winform 控件使用 lua 腳本中的中文還是正常的。

后面我只能在 Form1 中添加一個(gè) Print 方法,這樣打印才是正常的。
Form1代碼:
using NLua;
using System;
using System.IO;
using System.Text;
using System.Windows.Forms;
namespace NLuaDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private Lua Luas = new Lua();
//lua 腳本的地址
private string LuaPath;
private void Form1_Load(object sender, EventArgs e)
{
Luas.State.Encoding = Encoding.UTF8;
LuaPath = $"{Application.StartupPath}\\main.lua";
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Luas.Close();
}
//熱重載 點(diǎn)擊事件
private void Button_HotUpdate_Click(object sender, EventArgs e)
{
LoadLua();
Luas.DoString("update()");
}
//抽獎(jiǎng) 點(diǎn)擊事件
private void Button_Raffle_Click(object sender, EventArgs e)
{
Luas.DoString("raffle()");
}
//加載 lua 腳本
private void LoadLua()
{
if (File.Exists(LuaPath))
{
Luas.LoadCLRPackage();
Luas.DoFile(LuaPath);
Luas["this"] = this;
}
}
//lua 打印用的
public void Print(string text)
{
Console.WriteLine(text);
}
}
}
運(yùn)行后,點(diǎn)擊 “熱重載” 按鈕,就會(huì)看到獎(jiǎng)品和抽獎(jiǎng)人都有那些內(nèi)容,這些都是由 lua 進(jìn)行賦值的。

點(diǎn)擊 “抽獎(jiǎng)” 按鈕,就可以看到當(dāng)前的中獎(jiǎng)人和獎(jiǎng)品

既然是熱更新,那我在運(yùn)行過程中改變代碼可以么?當(dāng)然可以!
我們把 lua 腳本中抽獎(jiǎng)人和獎(jiǎng)品表單刪除一部分,改完后記得保存,如下:
--獎(jiǎng)品
prize = {"手機(jī)", "電腦", "耳機(jī)", "鼠標(biāo)"}
--抽獎(jiǎng)人
participant = {"張三", "李四", "老王"}再次點(diǎn)擊 “熱重載” 按鈕,這時(shí)界面就發(fā)生了一些變化

抽獎(jiǎng)人和獎(jiǎng)品都少了

NLua 其實(shí)還有很多其他的用法,這里就沒一一展示了。
到此這篇關(guān)于C# NLua Winform實(shí)現(xiàn)熱更新的項(xiàng)目實(shí)踐的文章就介紹到這了,更多相關(guān)C# NLua Winform 熱更新內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
unity實(shí)現(xiàn)鼠標(biāo)跟隨(ITween)
這篇文章主要為大家詳細(xì)介紹了unity實(shí)現(xiàn)鼠標(biāo)跟隨,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04
C#基于FTP協(xié)議的簡(jiǎn)易軟件自動(dòng)升級(jí)程序
這篇文章主要為大家詳細(xì)介紹了C#基于FTP協(xié)議的簡(jiǎn)易軟件自動(dòng)升級(jí)程序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03
在C#項(xiàng)目中調(diào)用C++編寫的動(dòng)態(tài)庫的三種方式
這篇文章給大家介紹了三種方式詳解如何在C#項(xiàng)目中調(diào)用C++編寫的動(dòng)態(tài)庫,文中通過代碼示例給大家介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2024-01-01
基于Unity3D實(shí)現(xiàn)3D迷宮小游戲的示例代碼
迷宮游戲作為經(jīng)典的小游戲,一直深受大家的喜愛。本文小編將為大家詳細(xì)介紹一下如何用Unity實(shí)現(xiàn)一個(gè)3D版的迷宮小游戲,感興趣的可以動(dòng)手試一試2022-03-03
C# WPF實(shí)現(xiàn)讀取文件夾中的PDF并顯示其頁數(shù)的操作指南
文章介紹了利用WPF和AI工具Gemini開發(fā)PDF整理工具的過程,采用iText處理PDF、NPOI導(dǎo)出Excel,結(jié)合MVVM模式實(shí)現(xiàn)UI與邏輯分離,并解決轉(zhuǎn)換器錯(cuò)誤問題,最終完成文件信息展示與批量處理功能,需要的朋友可以參考下2025-09-09

