Lua是一种很好的扩展性语言,Lua解释器被设计成一个很容易嵌入到宿主程序的库。LuaInterface则用于实现Lua和CLR的混合编程。

(一)Lua from the CLR

  测试环境:在VS2010中建一个C#控制台应用程序,并添加LuaInterface.dll的引用(安装LuaForWindows或直接下载LuaInterface都可得到该文件)。

  LuaForWindows下载地址:http://luaforge.net/projects/luaforwindows/

  LuaInterface下载地址:http://luaforge.net/projects/luainterface/ (下载luainterface-1.5.3,这里面的资源比较多,还有比较完整的示例代码,很有用的哦。)

  LuaInterface.Lua类是CLR访问Lua解释器的主要接口,一个LuaInterface.Lua类对象就代表了一个Lua解释器(或Lua执行环境),Lua解释器可以同时存在多个,并且它们之间是完全相互独立的。

  下面的简单代码展示了以下功能:

  (1)CLR访问Lua的全局域: 下标/索引操作[]

  (2)CLR新建Lua的table:NewTable

  (3)CLR中执行Lua脚本代码或脚本文件:DoFile、DoString

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LuaInterface; namespace TestCSharpAndLuaInterface
{
static void Main(string[] args)
{
// 新建一个Lua解释器,每一个Lua实例都相互独立
Lua lua = new Lua(); // Lua的索引操作[]可以创建、访问、修改global域,括号里面是变量名
// 创建global域num和str
lua["num"] = ;
lua["str"] = "a string"; // 创建空table
lua.NewTable("tab"); // 执行lua脚本,着两个方法都会返回object[]记录脚本的执行结果
lua.DoString("num = 100; print(\"i am a lua string\")");
lua.DoFile("C:\\luatest\\testLuaInterface.lua");
object[] retVals = lua.DoString("return num,str"); // 访问global域num和str
double num = (double)lua["num"];
string str = (string)lua["str"]; Console.WriteLine("num = {0}", num);
Console.WriteLine("str = {0}", str);
Console.WriteLine("width = {0}", lua["width"]);
Console.WriteLine("height = {0}", lua["height"]);
}
}
}

LuaIntrface自动对应Lua和CLR中的一些基础类型
  [nil, null]
  [string, System.String]
  [number, System.Double]
  [boolean, System.Boolean]
  [table, LuaInterface.LuaTable]
  [function, LuaInterface.LuaFunction]
以上对应关系反之亦然。

特殊类型:userdata

  (1)CLR中不能自动匹配Lua类型的对象(以上基础类型之外的类型)传给Lua时,转换为userdata,当Lua把这些userdata传回给CLR时,这些userdata又转换回原类型对象;
  (2)Lua里面生成的userdata从Lua传到CLR时转换为LuaInterface.LuaUserData。

  LuaTable和LuaUserData都有索引操作[],用来访问或修改域值,索引可以为string或number。
  LuaFunction和LuaUserData都有call方法用来执行函数,可以传入任意多个参数并返回多个值。

Lua调用CLR的函数:RegisterFunction方法用来将CLR函数注册进Lua解释器,供Lua代码调用,看下面这个例子:

namespace TestCSharpAndLuaInterface
{
class TestClass
{
private int value = ; public void TestPrint(int num)
{
Console.WriteLine("TestClass.TestPrint Called! value = {0}", value = num);
} public static void TestStaticPrint()
{
Console.WriteLine("TestClass.TestStaticPrint Called!");
}
} class Program
{
static void Main(string[] args)
{
Lua lua = new Lua(); TestClass obj = new TestClass();
// 注册CLR对象方法到Lua,供Lua调用
lua.RegisterFunction("LuaTestPrint", obj, obj.GetType().GetMethod("TestPrint")); // 也可用 typeof(TestClass).GetMethod("TestPrint")
// 注册CLR静态方法到Lua,供Lua调用
lua.RegisterFunction("LuaStaticPrint", null, typeof(TestClass).GetMethod("TestStaticPrint")); lua.DoString("LuaTestPrint(10)");
lua.DoString("LuaStaticPrint()");
}
}
}

(二)CLR from Lua

(1)加载和实例化CLR类型

测试环境有两种方式:

  第一种:纯Lua文件中进行测试

  将LuaForWindows安装的LuaInterface.dll和luanet.dll都拷贝到自己注册的环境变量的目录下,比如我的是"C:\\luatest\\",然后就可以在Lua编辑器中编写测试代码了,如下:

--package.cpath  = "C:\\luatest\\?.dll"

require "luanet"

--加载CLR的类型、实例化CLR对象
luanet.load_assembly("System.Windows.Forms")
luanet.load_assembly("System.Drawing")
Form = luanet.import_type("System.Windows.Forms.Form")
StartPosition = luanet.import_type("System.Windows.Forms.FormStartPosition") print(Form)
print(StartPosition)

  上面的代码演示了如果利用LuaInterface的luanet在Lua中加载CLR的类型。在配置编译环境的时候一定要注意将两个dll同时拷贝到一个目录下,因为luanet.dll是依赖LuaInterfce.dll的。

  第二种:在C#工程中测试

  还是在外部单独编写lua代码文件,然后在C#工程中使用lua.DoFile接口运行lua代码。这种方式比较灵活并且能够更方便的测试LuaInterface所提供的各项功能,我们后面的测试代码均是在这种模式系下进行测试。

  这种模式下就不需要在lua脚本中手动require "luanet"了,因为已经手动将LuaInterface的引用添加到工程中了,lua脚本中直接使用luanet就可以访问各接口了。

  luanet.load_assembly函数:加载CLR程序集;

  luanet.import_type函数:加载程序集中的类型;

  luanet.get_constructor_bysig函数:显示获取某个特定的构造函数;

  c#主要代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LuaInterface; namespace TesLuaInterface
{
class TestClass2
{
public TestClass2(string str)
{
Console.WriteLine("called TestClass2(string str) str = {0}", str);
} public TestClass2(int n)
{
Console.WriteLine("called TestClass2(int n) n = {0}", n);
} public TestClass2(int n1, int n2)
{
Console.WriteLine("called TestClass2(int n1, int n2) n1 = {0}, n2 = {1}", n1, n2);
}
} // 加载和实例化CLR类型
static void Main(string[] args)
{
Lua lua = new Lua(); lua.DoFile("C:\\luatest\\testLuaNet.lua");
}
}
}

  lua主要代码如下:

-- 加载自定义类型,先加载程序集,在加载类型
luanet.load_assembly("TestEnvi")
TestClass = luanet.import_type("TesLuaInterface.TestClass2") obj1 = TestClass(, ) -- 匹配public TestClass2(int n1, int n2)
obj2 = TestClass("x") -- 匹配public TestClass2(string str)
obj3 = TestClass() -- 匹public TestClass2(string str) TestClass_cons2 = luanet.get_constructor_bysig(TestClass, 'System.Int32')
obj3 = TestClass_cons2() -- 匹配public TestClass2(int n)

  TestEnvi为我建的工程代码的程序集名字,这一项是可以在工程属性中进行设置的,TestLuaInterface为测试代码的命名空间。

  从上面的构造函数的匹配可以看出,LuaInterface匹配构造函数的规律:

  LuaInterface匹配第一个能够匹配的构造函数,在这个过程中,numerical string(数字字符串)会自动匹配number,而number可以自动匹配string,所以TestClass(3)匹配到了参数为string的构造函数。

  如果一定要手动匹配某个构造函数,则可以使用luanet.get_constructor_bysic函数。

(2)访问CLR类型对象的字段和方法

  Lua代码中,访问CLR类型对象的字段的方式和访问table的键索引一样,比如button1.Text、button["Text"];

  Lua代码中,访问CLR类型对象的函数的方式和调用table的函数一样,比如form:ShowDialog()。

  规则很简单,但在访问函数的时候,有以下几种情况需要注意的:

  (a)当有重载函数时,函数的匹配过程和上面提到的构造函数的匹配过程一样,自动匹配第一个能够匹配的函数。如果一定要手动调用某个特定参数的函数,可以使用luanet.get_method_bysig函数来制定,比如:

  setMethod=get_method_bysig(obj,'setValue','System.String')"
  setMethod('str')

  (b)当函数有out或ref参数时,out参数和ref参数和函数的返回值一起返回,并且调用函数时out参数不需要传入,比如:

    -- calling int obj::OutMethod1(int,out int,out int)
retVal,out1,out2 = obj:OutMethod1(inVal)
-- calling void obj::OutMethod2(int,out int)
retVal,out1 = obj:OutMethod2(inVal) -- retVal ser´a nil
-- calling int obj::RefMethod(int,ref int)

  (c)如果一个对象有两个Interface,并且两个Interface都有某个同名函数比如,IFoo.method()和IBar.method(),这种情况下obj["IFoo.method"]表示访问前者。

  访问CLR类型对象的字段和函数的示例代码如下:

luanet.load_assembly("System.Windows.Forms")
luanet.load_assembly("System.Drawing")
Form = luanet.import_type("System.Windows.Forms.Form")
Button = luanet.import_type("System.Windows.Forms.Button")
Point = luanet.import_type("System.Drawing.Point")
StartPosition = luanet.import_type("System.Windows.Forms.FormStartPosition") form1 = Form()
button1 = Button()
button2 = Button()
position = Point(, )
start_position = StartPosition.CenterScreen button1.Text = "OK"
button2["Text"] = "Cancel"
button1.Location = position
button2.Location = Point(button1.Left, button1.Height + button1.Top + )
form1.Controls:Add(button1)
form1.Controls:Add(button2)
form1.StartPosition = start_position
form1:ShowDialog()

(3)事件处理,添加和删除事件委托

  LuaInterface为Event提供了Add和Remove函数来注册和移除事件处理函数。Add函数传入一个Lua函数,将其转换为一个CLR委托(delegate),并返回这个委托。

function handle_mouseup(sender,args)
  print(sender:ToString() .. ’ MouseUp!’)
  button.MouseUp:Remove(handler)
end
handler = button.MouseUp:Add(handle_mouseup)

(4)LuaInterface三种扩展CLR的方法

  LuaInterface提供了三种扩展CLR的方法,第一种就是上面提到的添加委托的方式,在需要delegate的地方传入Lua function,LuaInterface利用Lua function创建一个CLR delegate 并传入CLR。  

  第二种是在需要CLR Interface实例的地方传入一个Lua Table,比如:

-- interface ISample { int DoTask(int x, int y); }
-- SomeType.SomeMethod signature: int SomeMethod(ISample i)
-- obj is instance of SomeType
sum = {}
function sum:DoTask(x,y)
return x+y
end
-- sum is converted to instance of ISample
res = obj:SomeMethod(sum)

  如果Interface里面有多个重载接口,那么Lua Table需要实现每一个版本的接口函数,并且要注意out和ref参数的处理:

-- interface ISample2 { void DoTask1(ref int x, out int y);
-- int DoTask2(int x, out int y); }
-- SomeType.SomeMethod signature: int SomeMethod(ISample i)
-- obj is instance of SomeType
inc = {}
function inc:DoTask1(x)
return x+,x
end
function inc:DoTask2(x)
return x+,x
end
res = obj:SomeMethod(sum)

  第三种是利用Lua Table继承CLR Class,也就是用Table作为其子类,这里CLR Class必须拥有virtual函数,并且Lua Table必须至少重写一个virtual函数。主要相关函数是luanet.make_object。

-- class SomeObject {
-- public virtual int SomeMethod(int x, int y) { return x+y; } }
-- SomeType.SomeMethod signature: int SomeMethod(SomeObject o)
-- obj is instance of SomeType
some_obj = { const = }
function some_obj:SomeMethod(x,y)
local z = self.base:SomeMethod(x,y)
return z*self.const
end
SomeObject = luanet.import_type(’SomeObject’)
luanet.make_object(some_obj,SomeObject)
res = some_obj:SomeMethod(,) -- returns 20
res = some_obj:ToString() -- calls base method
res = obj:SomeMethod(some_obj) -- passing as argument

  因为Table作为子类实例,那么就可以在需要Class的地方传入这个Table实例。注意,如果Table没有重写任何virtual函数,则直接返回父类对象。当然,作为子类,可以直接访问父类中其他的还接口。

  以上三种归纳起来就是:Lua Function-->CLR delegate、Lua Table-->CLR Interface、 Lua Table-->CLR Class。

LuaInterface简介的更多相关文章

  1. 整理Lua和Unity和Lua交互文章链接

    重点文章: 1.[Unity3D]Unity3D游戏开发之Lua与游戏的不解之缘(上) 2.[Unity3D]Unity3D游戏开发之Lua与游戏的不解之缘(中) 3.Lua和C++交互详细总结 4. ...

  2. [整理]Unity3D游戏开发之Lua

    原文1:[Unity3D]Unity3D游戏开发之Lua与游戏的不解之缘(上) 各位朋友,大家好,我是秦元培,欢迎大家关注我的博客,我地博客地址是blog.csdn.net/qinyuanpei.如果 ...

  3. uLua学习之创建游戏对象(二)

    前言 上节,刚刚说到创建一个“HelloWorld”程序,大家想必都对uLua有所了解了,现在我们一步步地深入学习.在有关uLua的介绍中(在这里),我们可以发现它使用的框架是Lua + LuaJIT ...

  4. ASP.NET Core 1.1 简介

    ASP.NET Core 1.1 于2016年11月16日发布.这个版本包括许多伟大的新功能以及许多错误修复和一般的增强.这个版本包含了多个新的中间件组件.针对Windows的WebListener服 ...

  5. MVVM模式和在WPF中的实现(一)MVVM模式简介

    MVVM模式解析和在WPF中的实现(一) MVVM模式简介 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在 ...

  6. Cassandra简介

    在前面的一篇文章<图形数据库Neo4J简介>中,我们介绍了一种非常流行的图形数据库Neo4J的使用方法.而在本文中,我们将对另外一种类型的NoSQL数据库——Cassandra进行简单地介 ...

  7. REST简介

    一说到REST,我想大家的第一反应就是“啊,就是那种前后台通信方式.”但是在要求详细讲述它所提出的各个约束,以及如何开始搭建REST服务时,却很少有人能够清晰地说出它到底是什么,需要遵守什么样的准则. ...

  8. Microservice架构模式简介

    在2014年,Sam Newman,Martin Fowler在ThoughtWorks的一位同事,出版了一本新书<Building Microservices>.该书描述了如何按照Mic ...

  9. const,static,extern 简介

    const,static,extern 简介 一.const与宏的区别: const简介:之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽成宏,推荐我们使用const常量. 执行时刻:宏是预编 ...

随机推荐

  1. iOS-OC内存管理

    目标 1.[理解]内存管理 2.[掌握]第一个MRC程序 3.[掌握]内存管理的原则 4.[理解]野指针与僵尸对象 5.[理解]单个对象的内存管理 6.[理解]多个对象的内存管理 7.[掌握]set方 ...

  2. iOS-程序发布-32位和64位系统的兼容

    在苹果推出iPhone5S时,64位的应用就走到了眼前.当时就看见苹果官方资料宣布iOS7.x的SDK支持了64位的应用,而且内置的应用都已经是64位. 我记得自己刚刚接触电脑时还有16位的系统,指针 ...

  3. node.js WebService异常处理(domain)以及利用domain实现request生命周期的全局变量

    成熟的Web Service技术,例如Fast CGI.J2EE.php,必然会对代码异常有足够的保护,好的Web必然会在出错后给出友好的提示,而不是莫名其妙的等待504超时.而node.js这里比较 ...

  4. 两两组合覆盖测试用例设计工具:PICT

    两两组合覆盖测试用例设计工具:PICT 2016-08-31 目录 1 成对测试简介2 PICT使用  2.1 安装 PICT  2.2 使用PICT3 PICT算法  3.1 准备阶段  3.2 产 ...

  5. 微软BI 之SSIS 系列 - 通过设置 CheckPoints 检查点来增强 SSIS Package 流程的重用性

    开篇介绍 通常一个 ETL Package 是由多个控制流和数据流共同组成,有的时候 ETL 的步骤可能会比较多,整个流程执行下来的时间可能比较长.假设在 ETL Package 中包含5个Task, ...

  6. js for in对象key排序

    最近遇到一个比较奇葩的事情,for in 循环的时候,如果对象key 默认可以转化为整数,会把key转化为整数排序. code: 运行结果 大家看到data2的key默认顺序是"2" ...

  7. 查看Exchange邮件队列(queue)

    #加载Exchange管理模块 Add-PSSnapin Microsoft.Exchange.Management.PowerShell.snapin get-queue #查看队列 get-mes ...

  8. Linux 中的grep命令单引号,不加任何参数以及双引号的作用

    单引号: 可以说是所见即所得:即将单引号内的内容原样输出,或者描述为单引号里面看到的是什么就会输出什么.单引号''是全引用,被单引号括起的内容不管是常量还是变量者不会发生替换. 双引号: 把双引号内的 ...

  9. Windows KB2984972安装后堵住了一个windows 7 桌面可以多个用户远程访问桌面的漏洞。

    之前网络上有方法可以实现2个用户同时使用一个windows 7,一个在终端,一个通过远程桌面. 安装了这个kb后,就无法同时登陆了,同一时间只有一个用户可以登陆windows 7

  10. 使用Microsoft.Office.Interop.Excel.Application xlApp 生成Excel

    object filePath = @"C:\" + DateTime.Now.ToShortDateString().Replace("-", "& ...