XLua个人学习——C#访问Lua

目录

0.引言

1.访问基本数据类型全局变量

2.访问全局的table

2.1.映射到class或struct

2.2.映射到一个interface

2.3.映射到List、Dictionary

2.4.映射到LuaTable类

3.访问全局函数

3.1映射到delegate

3.2映射到LuaFunction

4.官方使用建议


0.引言

本文是个人学习xLua中C#访问Lua的一些知识点总结。参考教程的是官方教程:

xLua/Assets/XLua/Doc/XLua教程.md at master · Tencent/xLua

1.访问基本数据类型全局变量

通过Lua解析器的Global表中的Get()泛型API,可以读取Lua中基本数据类型全局变量。

示例:

 luaenv.Global.Get("a")
 luaenv.Global.Get("b")
 luaenv.Global.Get("c")

2.访问全局的table

同样也是用上述的Get方法。那么我们table应该映射到c#中的什么类型呢?

2.1.映射到class或struct

定义一个class,有对应于table的字段的public属性,而且有无参数构造函数即可。xLua会帮你new一个对应类实例,并把对应的字段赋值过去。

示例:

student={
	name="骰子",
	age=20,
	id=5
}
class Student
{
    public string name;
    public int age;
    public int id;
}
public class Test : MonoBehaviour
{
    void Start()
    {
        LuaMgr.Instance.Init();
        LuaMgr.Instance.DoString("require('Main')");
        LuaTable _G = LuaMgr.Instance._G;
        Student student = _G.Get("student");
        print(student.name);    //"骰子"
        print(student.age);    //20
        print(student.id);    //5
    }
}

也就是说xLua会new 一个Student实例来,并把table表中字段赋值到实例中对应的字段中去。

这里使用的LuaMgr是自己封装的一个管理lua解析器的类,可以去这篇文章查看:

基于xLua实现一个简单的Lua解析器管理器-CSDN博客

table的属性可以多于或者少于class的属性,并且可以嵌套其它复杂类型,而且table映射到类或结构体是值拷贝,这意味着修改class的字段值不会同步到table,反过来也不会。

实例:

student={
	name="骰子",
	age=20,
	id=5,
	testMore="多了"--测试table中有class没有的字段会不会报错
	,
	score={
		math=80,
		english=90,
		chinese=95
	}--测试嵌套table
}
struct Score
{
    public int math;
    public int chinese;
    public int english;
}
class Student
{
    public string name;
    public int age;
    public int id;
    public string testLess = "少了";//测试class多了table没有的字段会不会报错
    public Score score;//测试嵌套table可不可以接收到
}
public class Test : MonoBehaviour
{
    void Start()
    {
        LuaMgr.Instance.Init();
        LuaMgr.Instance.DoString("require('Main')");
        LuaTable _G = LuaMgr.Instance._G;
        Student student = _G.Get("student");
        print(student.name); //输出骰子
        print(student.age); //输出20
        print(student.id);//输出5
        print(student.testLess);//输出"少了",测试说明Student哪怕有table没有的字段也不会报错
        print(student.score.math);//输出80
        print(student.score.chinese);//输出95
        print(student.score.english);//输出90
        //测试说明嵌套table类型也可以直接接收
        LuaMgr.Instance.DoString("student.name='Cheer'"); //修改lua中的table数据
        LuaMgr.Instance.DoString("print(student.name)");//输出Cheer
        print(student.name);//输出骰子
        //这说明改变lua中的table数据不会改变student的数据,反过来也是这样的
        //这个过程是值拷贝
    }
}

根据上述示例测试,证实了上述结论。

注意:因为这个过程是值拷贝,所以如果class比较复杂代价会比较大。

2.2.映射到一个interface

接口不允许有成员变量,所以是用属性来进行table字段的接收。

这种方式依赖于生成代码(如果没生成代码会抛InvalidCastException异常),代码生成器会生成这个interface的实例,如果get一个属性,生成代码会get对应的table字段,如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数。

声明的interface必须要加[CSharpCallLua]特性,并且一旦改变了interface结构就需要重新生成代码,才能使用。

table的属性可以多于或者少于interface的属性,并且可以嵌套其它复杂类型。

嵌套的接口类型也需要加[CSharpCallLua]特性,也需要重新生成代码。

嵌套的非接口类型,例如类、结构体无需遵循接口映射的规则。

table映射到interface,是引用拷贝。

这里我个人注意到一个点,貌似[CSharpCallLua]特性修饰的delegate或是interface必须是public的,否则生成代码无效。

这种映射是xLua官方比较推荐使用的。

示例:

public struct Score
{
    public int math;
    public int chinese;
    public int english;
}
[CSharpCallLua]
public interface _GameObject
{
    int ID
    {
        get;set;
    }
    string Name
    {
        get;
        set;
    }
    string More
    {
        get;set;
    }
    Score score { get; set; }
}
public class Test : MonoBehaviour
{
    void Start()
    {
        LuaMgr.Instance.Init();
        LuaMgr.Instance.DoString("require('Main')");
        LuaTable _G = LuaMgr.Instance._G;
        #region 映射到interface
        LuaMgr.Instance.DoString(@"
        gameObject={
            Name = 'Cheer',
            ID= 105,
            positionX=1.5,
            positionY=1.5,
            score={
                math=90,
                english=0,
                chinese=100
            }
        }
        ");
        _GameObject gameObject =  _G.Get<_GameObject>("gameObject");
        print(gameObject.ID);//105
        print(gameObject.Name);//Cheer
        print(gameObject.More);//Null
        print(gameObject.score.math);//输出90
        print(gameObject.score.chinese);//输出100
        print(gameObject.score.english);//输出0

        //table映射到interface是值拷贝吗?
        //测试一下
        LuaMgr.Instance.DoString("gameObject['Name']='骰子'");
        print(gameObject.Name);//输出骰子   这说明改变table表,接口实例也跟着发生了改变
        gameObject.Name = "_cheer";
        LuaMgr.Instance.DoString("print(gameObject.Name)");//输出_cheer 这说明改变接口实例,table表也跟着发生了改变
        //这说明table映射到interface是引用拷贝。
        #endregion
    }
}
2.3.映射到List、Dictionary

对于table模拟的数组,可以用List进行映射。

如果table存储不定类型,可以用List来接收

table映射到List,是值拷贝过程。

示例:

  LuaMgr.Instance.DoString(@"
        testList={1,2,3,4,5,6}
        testList2={'哈吉米',2,false,4,1.2}
        ");
        List list = _G.Get>("testList");
        foreach (int i in list) print(i);//输出1 2 3 4 5 6 映射正常
        List list2 = _G.Get>("testList2");//对于不定类型可以用object读
        foreach (object i in list2) print(i);//输出'哈吉米' 2 false 4 1.2 映射正常
        //List映射是不是值拷贝呢?
        //测试一下
        list2[0] = "骰子";
        LuaMgr.Instance.DoString("print(testList2[1])"); //输出 哈吉米 说明 List映射还是值拷贝 
  

字典也是类似的用法。

如果table的键存储了不定类型,用Dictionary来接收;

如果table的值存储了不定类型,用Dictionary来接收;

table映射到字典,是值拷贝过程。

示例:

        LuaMgr.Instance.DoString(@"
        testDic={
            ['1']=1,
            ['2']=2,
            ['3']=3,
        }
        testDic2={
            name = 12,
            [false]='哇哇哇',
            ['1']=false
        }        
        ");
        Dictionary testDic = _G.Get>("testDic");
        foreach(string key in testDic.Keys)
        {
            print(key + ":" + testDic[key]);//输出 1:1 2:2 3:3 映射正常
        }
        Dictionary testDic2 = _G.Get>("testDic2");
        foreach (object key in testDic2.Keys)
        {
            print(key + ":" + testDic2[key]);//映射正常
        }
        //Dictionary映射是不是值拷贝呢?
        //测试一下
        LuaMgr.Instance.DoString("testDic['1']=2");
        print(testDic["1"]);//输出 2 说明 Dictionary映射还是值拷贝

不想定义class或者interface的话,可以考虑用这个,前提table下key和value的类型都是一致的,否则用object装会引起装箱拆箱问题。

2.4.映射到LuaTable类

table映射到LuaTable也是引用拷贝,其语法简单且不需要生成代码。

实例:

        LuaMgr.Instance.DoString(@"
        score={
                math=90,
                english=0,
                chinese=100
            }        
        ");
        LuaTable luaTable = _G.Get("score");
        print(luaTable.Get("math"));//输出90
        print(luaTable.Get("english"));//输出0
        print(luaTable.Get("chinese"));//输出100
        luaTable.Set("math", 55);//设置table值
        LuaMgr.Instance.DoString("print(score.math)");//lua中的score.math也发生了改变
        //这种映射是引用拷贝.

这种方式虽然有以上好处,但也有一些问题(官方不推荐使用),比如慢——比映射到一个interface要慢一个数量级;比如没有类型检查。

注意:LuaTable需要手动Dispose,或者调用lua解析器的Tick释放垃圾,否则会一直占用内存造成内存泄漏。

3.访问全局函数

仍然是用Get方法,不同的是类型映射。

3.1映射到delegate

这种是官方建议的方式,性能好很多,而且类型安全。

缺点是要手动加给委托加[CSharpCallLua]特性并生成代码(如果没生成代码会抛InvalidCastException异常)。

声明规则:delegate的输入参数对应function的参数;多返回值从左往右映射到C#的输出参数(包括返回值、out参数、ref参数)。

支持类型:各种复杂类型,含out、ref修饰的,甚至可返回另一个delegate。

delegate的使用就更简单了,直接像个函数那样用就可以了。

示例:

    [CSharpCallLua]
    public delegate int luaDelegate(int a, int b, out int Sum);
    void Start()
    {
        LuaMgr.Instance.Init();
        LuaMgr.Instance.DoString(@"
           function AddAndReturn(a, b) 
                local sum = a + b 
                return sum, sum * 2 
            end 
        ");
        //映射到delegate
        luaDelegate _luaDelegate = LuaMgr.Instance._G.Get("AddAndReturn");
        int Sum;
        int sum =_luaDelegate(2,3, out Sum);
        print(sum+"and"+Sum);//输出6and12
    }
3.2映射到LuaFunction

这种方式的优缺点刚好和第一种相反。 使用也简单,LuaFunction上有个变参的Call函数,可以传任意类型,任意个数的参数,返回值是object的数组,对应于lua的多返回值。

        //映射到LuaFunction
        LuaFunction luaFunction = LuaMgr.Instance._G.Get("AddAndReturn");
        object[] results = luaFunction.Call(new object[]{2,3 });
        for(int i=0; i

4.官方使用建议

访问lua全局数据,特别是table以及function,代价比较大,建议尽量少做,比如在初始化时把要调用的lua function获取一次(映射到delegate)后,保存下来,后续直接调用该delegate即可。table也类似。

如果lua侧的实现的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一个专门的模块负责xlua的初始化以及delegate、interface的映射,然后把这些delegate和interface设置到要用到它们的地方。

你可能感兴趣的:(学习,lua,开发语言,xLua)