由于之前几天一直在忙于之前软件功能的修改和bug修复,所以一直没有更新,今天正好有空,补充一下lua在.net中使用的第二篇,这篇文章会比较长,将原本两篇的内容合并到此篇。
Lua本身就是一个嵌入式语言学习曲线很低,假如您曾使用过python或者php之类的编程语言,通过一个小时左右的学习就可以掌握此语言。在此我将不会介绍Lua语言的详细语法——推荐看一下Lua 教程 (w3schools.cn),将在此讲述一下NLua的真实使用体验。
首先安装NLua包到项目中
dotnet add package NLua
你的软件现在可以启用NLua的环境,通过lua命令的字符串给程序带来灵活响应用户需求变更的能力了。
NLua使用的具体API可以参考官方在github中的文档,NLua官方文档,文档非常详细的使用了nlua中的使用。
让NLua支持中文,请将其编码格式转换为UTF-8
using (Lua lua = new Lua())
{
lua.State.Encoding = Encoding.UTF8;
lua.DoString("res = 'Файл'");
string res = (string)lua["res"];
Assert.AreEqual("Файл", res);
}
创建Lua环境
using NLua;
Lua state = new Lua ()
计算简单表达式
var res = state.DoString ("return 10 + 3*(5 + 2)")[0] as double;
执行结果将返回一个数组,需要根据语义,将运行结果转换为对应的.net值类型实例。
将.net值传递给状态:
double val = 12.0;
state ["x"] = val; // Create a global value 'x'
var res = (double)state.DoString ("return 10 + x*(5 + 2)")[0];
检索全局值:
state.DoString ("y = 10 + x*(5 + 2)");
double y = (double) state ["y"]; // Retrieve the value of y
通过索引器,可以访问到Lua中已经存储的对象,对象可以是值对象,也可以是array或者table
检索 Lua 函数:
state.DoString (@"
function ScriptFunc (val1, val2)
if val1 > val2 then
return val1 + 1
else
return val2 - 1
end
end
");
var scriptFunc = state ["ScriptFunc"] as LuaFunction;
var res = (int)scriptFunc.Call (3, 5).First ();
// LuaFunction.Call will also return a array of objects, since a Lua function
// can return multiple values
通过索引器访问Lua函数,并通过Call调用此函数。同时Lua中的函数也是一个变量,因此上面的ScriptFunc函数也可以如此访问。Call调用函数返回的也是一个数组,可以通过对数组元素的访问,获取返回值。
state.DoString (@"
scriptfuncval = function ScriptFunc (val1, val2)
if val1 > val2 then
return val1 + 1
else
return val2 - 1
end
end
");
var scriptFunc = state ["scriptfuncval"] as LuaFunction;
var res = (int)scriptFunc.Call (3, 5).First ();
// LuaFunction.Call will also return a array of objects, since a Lua function
// can return multiple values
将 .NET 对象传递给状态:
SomeClass obj = new SomeClass ("Param");
state ["obj"] = obj;
// Create a global value 'obj' of .NET type SomeClass
// This could be any .NET object, from BCL or from your assemblies
假如SomeClass有一个函数SomeFunction(int i),那么在Lua中调用此函数如下:
state.DoString("res=obj:SomeFunction(1)")
加入SomeClass有一个静态函数StaticFunction(int i),在Lua中如下调用:
state.DoString (@"local res4 = SomeClass.StaticMethod(4)");
local代表是本地变量,使用了local前缀的变量,不能通过NLua的索引器在.net语句中进行访问。
接下来我们来看一个真实案例中NLua中的应用。
在开发过程中,我们遇到了这样一个问题,用户希望绘制一条曲线,这条曲线的x坐标值,可以需要根据给出的公式进行计算。这样我们就需要提供给客户一个接口,让他通过输入一个表达式,随时改变曲线的绘制方式。
需求如下,取一段时间的值,再通过输入的多项式拟合计算出x坐标值。可能的公式:
res=avg(arr)^3*a+avg(arr)^2*b+avg(arr)*c+d
也可能是:
res=max(arr)*2*10+3
此时,我们引入NLua可以很轻松的通过将决定权给用户来实现这个需求。在此处,有两个知识点需要说明:lua的math库,以及.net的数组如何在lua中访问。
lua中引入math库
state.DoString(@"math=require 'math'")
state.DoString(@"math.max(arr)")
.net数组对象赋值给lua对象并不能正常的当作lua数组进行访问,在table.unpack的解包过程中会抛出异常。
double[] vals = new double[5] { 1, 2, 3, 4, 5 };
state["arr"] = vals;
state.DoString(@"math=require 'math'
maxVal=math.max(table.unpack(arr))");
var obj = state["maxVal"];
//thorw exception attempt to get length of a System.Double[]
查看了官方的案例,并没有给出解决方案,可能是一个BUG。我提交了一个issue给到list,目前没有收到官方的回复。
我为Array添加了扩展函数ToLuaTable,
public static class ArrayExtensions
{
public static NLua.LuaTable ToLuaTable(this Array array, Lua state, string name)
{
state.DoString(name + "={}");
LuaTable table = state.GetTable(name);
for (int i = 0; i < array.Length; i++)
{
table[i] = array.GetValue(i);
}
return table;
}
}
通过它,我将.net数组转换成了Lua数组,这样就可以执行函数过程中得到预期结果了。
double[] vals = new double[5] { 1, 2, 3, 4, 5 };
vals.ToLuaTable(state,"arr");
state.DoString(@"math=require 'math'
maxVal=math.max(table.unpack(arr))");
var obj = state["maxVal"];
如果我的文章对您有所帮助,请您为我的文章点赞,让更多人可以看到,谢谢。