• LuaInterface简介


      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"] = 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#工程中测试

      还是在外部单独编写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(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为我建的工程代码的程序集名字,这一项是可以在工程属性中进行设置的,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(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。

  • 相关阅读:
    canvas 实现环形进度条
    wx 小程序 text 文本   字数多了会自动换行的问题
    微信小程序 checked 选择功能 js中获取对应的选择值的改变以及修改
    微信小程序 scroll-view 横向滚动问题
    版本三开始看一边
    wx小程序 更新数组中的对象这样类型的写法
    react
    微信小程序checkbox样式和background修改
    ES6扩展——模板字符串
    函数参数的解构赋值
  • 原文地址:https://www.cnblogs.com/sifenkesi/p/3901831.html
Copyright © 2020-2023  润新知