LuaInterface简单介绍

Lua是一种非常好的扩展性语言。Lua解释器被设计成一个非常easy嵌入到宿主程序的库。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"] = 2;
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 = 0; 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#project中測试

  还是在外部单独编写lua代码文件。然后在C#project中使用lua.DoFile接口执行lua代码。这样的方式比較灵活而且可以更方便的測试LuaInterface所提供的各项功能,我们后面的測试代码均是在这样的模式系下进行測试。

  这样的模式下就不须要在lua脚本中手动require "luanet"了,由于已经手动将LuaInterface的引用加入到project中了。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(2, 3) -- 匹配public TestClass2(int n1, int n2)
obj2 = TestClass("x") -- 匹配public TestClass2(string str)
obj3 = TestClass(3) -- 匹public TestClass2(string str) TestClass_cons2 = luanet.get_constructor_bysig(TestClass, 'System.Int32')
obj3 = TestClass_cons2(3) -- 匹配public TestClass2(int n)

TestEnvi为我建的project代码的程序集名字,这一项是能够在project属性中进行设置的。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()。

  规则非常easy,但在訪问函数的时候,有下面几种情况须要注意的:

  (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(10, 10)
start_position = StartPosition.CenterScreen button1.Text = "OK"
button2["Text"] = "Cancel"
button1.Location = position
button2.Location = Point(button1.Left, button1.Height + button1.Top + 10)
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+1,x
end
function inc:DoTask2(x)
return x+1,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 = 4 }
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(2,3) -- 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. [原创]关于mybatis中一级缓存和二级缓存的简单介绍

    关于mybatis中一级缓存和二级缓存的简单介绍 mybatis的一级缓存: MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候 ...

  2. 利用Python进行数据分析(7) pandas基础: Series和DataFrame的简单介绍

    一.pandas 是什么 pandas 是基于 NumPy 的一个 Python 数据分析包,主要目的是为了数据分析.它提供了大量高级的数据结构和对数据处理的方法. pandas 有两个主要的数据结构 ...

  3. 利用Python进行数据分析(4) NumPy基础: ndarray简单介绍

    一.NumPy 是什么 NumPy 是 Python 科学计算的基础包,它专为进行严格的数字处理而产生.在之前的随笔里已有更加详细的介绍,这里不再赘述. 利用 Python 进行数据分析(一)简单介绍 ...

  4. yii2的权限管理系统RBAC简单介绍

    这里有几个概念 权限: 指用户是否可以执行哪些操作,如:编辑.发布.查看回帖 角色 比如:VIP用户组, 高级会员组,中级会员组,初级会员组 VIP用户组:发帖.回帖.删帖.浏览权限 高级会员组:发帖 ...

  5. angular1.x的简单介绍(二)

    首先还是要强调一下DI,DI(Denpendency Injection)伸手获得,主要解决模块间的耦合关系.那么模块是又什么组成的呢?在我看来,模块的最小单位是类,多个类的组合就是模块.关于在根模块 ...

  6. Linux的简单介绍和常用命令的介绍

    Linux的简单介绍和常用命令的介绍 本说明以Ubuntu系统为例 Ubuntu系统的安装自行百度,或者参考http://www.cnblogs.com/CoderJYF/p/6091068.html ...

  7. iOS-iOS开发简单介绍

    概览 终于到了真正接触IOS应用程序的时刻了,之前我们花了很多时间去讨论C语言.ObjC等知识,对于很多朋友而言开发IOS第一天就想直接看到成果,看到可以运行的IOS程序.但是这里我想强调一下,前面的 ...

  8. iOS开发多线程篇—多线程简单介绍

    iOS开发多线程篇—多线程简单介绍 一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcod ...

  9. iOS开发UI篇—UITabBarController简单介绍

    iOS开发UI篇—UITabBarController简单介绍 一.简单介绍 UITabBarController和UINavigationController类似,UITabBarControlle ...

随机推荐

  1. Sql Server 中锁的概念(1)

    Sql Server 中锁的概念   锁的概述 一. 为什么要引入锁 多个用户同时对数据库的并发操作时会带来以下数据不一致的问题: 丢失更新A,B两个用户读同一数据并进行修改,其中一个用户的修改结果破 ...

  2. 【干货分享】C# 实体类生成工具

    前言: 项目实战中不论是业务编码还是通用编码,总会归纳出一些通用的工具类.放入项目中一劳永逸,让兄弟姐妹们避免编写重复代码.所以利用了工作之余的时间,将这些散落在多个项目中精致优雅的工具类,归纳起来形 ...

  3. mysql 报错Authentication method 'caching_sha2_password' is not supported

    原文地址:https://blog.csdn.net/u011583336/article/details/80999043 之前工作中用的数据库多是ms sqlserver,偶尔用到mysql都是运 ...

  4. Mysql中max函数取得的值不是最大

    ①问题:遇到一个很有意思的问题,这里记录一下, 就是在使用max函数的时候发现取得的最大值其实不是最大值. 比如: 某一列中有10000000,和9999999, 其最大值应该是10000000但是查 ...

  5. INFORMATION_SCHEMA InnoDB 表

    INFORMATION_SCHEMA InnoDB Tables 本节提供InnoDB INFORMATION_SCHEMA表的表定义. 有关相关信息和示例,请参见"InnoDB INFOR ...

  6. 02-Mysql中的运算符

    Mysql中运算符 1.算术运算符运算符 作用+   加法-    减法*    乘法/,DIV     除法,返回商%,MOD       除法,返回余数 mysql root@localhost: ...

  7. 为公司架构一套高质量的 Vue UI 组件库

    有没有曾遇过,产品要我们实现一个功能,但是 iview 或者 elementui 不支持,我们然后义正言辞的说,不好意思,组件库不支持,没法做到. 有没有曾和设计师争论得面红耳赤,其实也是因为组件库暂 ...

  8. LeetCode(78) Subsets

    题目 Given a set of distinct integers, nums, return all possible subsets. Note: Elements in a subset m ...

  9. 《算法导论》— Chapter 11 散列表

    1 序 在很多应用中,都要用到一种动态集合结构,它仅支持INSERT.SEARCH以及DELETE三种字典操作.例如计算机程序设计语言的编译程序需要维护一个符号表,其中元素的关键字为任意字符串,与语言 ...

  10. linux 文件三大特殊权限(SUID SGID SBIT)

    SGID(这个应该是文件共享里面最常用权限管理手段) 作用于目录或可执行程序,作用于目录代表在此目录创建的文件或目录,默认的属组继承此目录的属组.例如 我这个testgroup 没有设置SGID .我 ...