文章参考来源:https://blog.csdn.net/heyuchang666/article/details/50722658
LuaInterface.Lua类是CLR访问Lua解释器的主要接口,一个LuaInterface.Lua类对象就代表了一个Lua解释器(或Lua执行环境),Lua解释器可以同时存在多个,并且它们之间是完全相互独立的。简单来说,LuaInterface就是用于Lua和C#交互的工具。
LuaInterface的下载地址:http://luaforge.net/projects/luainterface/。下载后解压即可获取两个dll文件:luanet.dll和luainterface.dll。
我使用的是VS2017新建的工程。加入两个dll文件的引用后,引用luainterface命名空间即可。此时如果实例化一个解释器实例,运行时会报错,如下:
这个问题的解决可以参见:https://blog.csdn.net/bbnbf/article/details/7010551。
解决问题后,开始学习之路。
lua和C#的交互无非就是lua调用C#的字段,方法。C#调用lua的方法,执行lua脚本。
-
C#执行lua脚本(创建变量,执行方法等)
可以使用索引操作符[]来创建变量,NewTable()用于创建一个空表格,要修改数据,需要用到lua对表的操作。例如:
class Program
{
static void Main(string[] args)
{
// 创建一个lua解释器。每个lua实例都是相互独立的
Lua lua = new Lua();
// lua的索引操作[]可以创建,访问,修改global域,括号里面是变量名
// 创建global域num和str两个变量
lua["num"] = 2;
lua["str"] = "this is a string";
// 创建空的table
lua.NewTable("tab");
}
}
DoString()方法可以执行一段lua代码,参数是一段string类型的lua代码片段。
class Program
{
static void Main(string[] args)
{
// 创建一个lua解释器。每个lua实例都是相互独立的
Lua lua = new Lua();
// lua的索引操作[]可以创建,访问,修改global域,括号里面是变量名
// 创建global域num和str两个变量
lua["num"] = 2;
lua["str"] = "this is a string";
// 创建空的table
lua.NewTable("tab");
// 执行lua脚本,这两个方法都会返回object[] 记录脚本的执行结果
lua.DoString("num = 100, print('i an a string')");
lua.DoString("local a = {1,2,3} " +
"for k,v in pairs(a) do " +
"print(k,v) " +
"end");
}
}
执行结果如下,可以看出,我们正确遍历了table。只不过,写起来有点怪怪的。
Dostring()方法会返回一个object[]类型的数组。可以获取lua中执行结果的返回。如下所示:
会发现,没有获取到表a的值。这是因为DoString()方法是执行的lua代码片段。这里的a和上一段的表a不是同一个变量。如果改成“return a = 10”,那么就会打印出结果10。
DoFile()方法用于执行lua脚本,参数是lua脚本的路径,如下:
LuaIntrface自动对应Lua和CLR中的一些基础类型:
[nil, null]
[string, System.String]
[number, System.Double]
[boolean, System.Boolean]
[table, LuaInterface.LuaTable]
[function, LuaInterface.LuaFunction]
以上对应关系反之亦然。如下图所示:
-
Lua执行C#脚本
RegisterFunction方法用来将CLR函数注册进Lua解释器,供Lua代码调用。该方法接收三个参数:string path, object target, MethodBase function。path指注册进lua的函数在lua中的名字, target指方法的对象实例,function指方法。如下,我们新建一个类,里面写一些打印输出方法:
注册进lua后,用DoString方法执行:
可以看到,方法被正确执行了。另外,我们看到,静态方法,第二个参数是不用传递的(null)。这里我们使用GetMethod()来获取类中的方法。RegisterFunction的返回值类型是LuaFunction类型。接收的是lua中的函数。如下,我们在LuaCallCSharpTest类中新增一个方法:
LuaFunction类型对应的是lua种的function。使用Call方法,就可以执行lua中的该方法。
-
CLR from Lua
这里我们在外部编写lua脚本,然后在C#中调用。Luainterface主要提供了下面几个方法:
luanet.load_assembly函数:加载CLR程序集;
luanet.import_type函数:加载程序集中的类型;
luanet.get_constructor_bysig函数:显示获取某个特定的构造函数;
我们可以通过这几个方法。加载C#代码到lua中去。加载指定程序集下面的指定类。
C#测试代码如下,我们写了几个不同的构造函数,然后尝试在lua中进行匹配。
编写lua脚本,脚本我是放在项目工程目录下的,
也可以将luanet.dll 拷贝到工程 DeBug 输出目录中,然后需要将写好的Lua文件拖到项目中,并且修改属性为 :如果较新则复制 / 始终复制。否则会出现找不到文件的 报错信息。这样也同样不需要require了。
lua脚本如下:
程序集的名字可以在项目中进行设置,在菜单的项目->XXX属性下面就可以设置了。import_type中就是命名空间的名字了。
从上面的构造函数的匹配可以看出,LuaInterface匹配构造函数的规律:
LuaInterface匹配第一个能够匹配的构造函数,在这个过程中,numerical string(数字字符串)会自动匹配number,而number可以自动匹配string,所以CSharpFromLua(3)匹配到了参数为string的构造函数。
如果一定要手动匹配某个构造函数,则可以使用luanet.get_constructor_bysic函数。
-
访问C#对象和字段
Lua代码中,访问CLR类型对象的字段的方式和访问table的键索引一样,比如button1.Text、button["Text"];
Lua代码中,访问CLR类型对象的函数的方式和调用table的函数一样,比如form:ShowDialog()。
C#测试代码和lua代码如下:
使用DoFile()执行lua文件,输出结果如下:
我们已经通过构造函数实例化了该类,所以只要直接调用方法即可。这里使用冒号操作符来调用类的方法,使用[]来访问类的字段,这一点和访问table是一样的。冒号和点号的使用区别可以在网上查到。
其他还有一些特殊情况:
当有重载方法时,匹配的规则就可上面介绍的一样。可以使用luanet.get_method_bysig方法来匹配指定的方法。如下图:
数字字符串类型的参数,会自动匹配int。而普通的字符串则会自动匹配string参数。
当函数有out或ref参数时,out参数和ref参数和函数的返回值一起返回,并且调用函数时out参数不需要传入,可以使用别的变量来接受该out参数,比如:
对于ref参数,如下:
可以看到,其在lua中的用法和out参数是一样的。但是,传递进去的参数并没有变化,这一点和在C#中的用法是不一样的。
-
事件处理,添加和删除事件委托
LuaInterface为Event提供了Add和Remove函数来注册和移除事件处理函数。Add函数传入一个Lua函数,将其转换为一个CLR委托(delegate),并返回这个委托。如下: