xlua中C#调用Lua的table和function

开头

    上一篇 文章主要介绍了xlua热更新中生成代码的原理,以及Lua调用C#对象的原理。Lua调用c#对象可以用两种方式(生成代码和反射)实现同一种结果:生成userdate保存对象在c#侧的索引,为所有成员方法生成包裹,利用Lua中已存在的其与C/C++压栈方式的数据传递,进行参数和返回值的传递,实现对c#对象的控制。
    那么,在使用热更新时,需要用Lua函数重载C#函数,这时就需要c#能够调用到Lua。这篇文章粗粗介绍下其中的原理。不足之处,请各位多多指正。后续会加上代码和详细步骤解析。

正文

    上一节曾提到过Lua和C/C++的数据交互是通过栈进行的,进行数据传递的时候,把数据拷贝到栈上,并返回其索引值,就可以在另一边取到数据。而c#就可以借助这种方式,通过P/Invoke(非托管代码调用)方式调用Lua的dll,执行Lua的C API,来完成与lua的数据通信。

    我们先看一下Lua中的数据结构:string、bool、number、table、function。string、bool和number可以在C#侧找到对应的数据结构进行转换接收,function在c#中对应着委托,table没有对应的结构,xlua自己定义了一个LuaTable的类。在ObjectCasters.cs中,有xlua默认定义的转换函数。
xlua中C#调用Lua的table和function_第1张图片

C#调用Lua table

    当给一个LuaTable(C#侧)对象赋值table(Lua侧)时,其lua代码相当于CS.XXX.luatable = { a=1 },首先在赋值时,lua这边会把参数{a=1}压入栈中,然后需要C#调用其生成的set包裹方法(见上一篇)进行设置。压入之后,c#这边就要先拿到这个table,需要先获取类型对应的转换器函数,将lua返回的栈索引取到table的引用,添加到Lua注册表中,然后LuaTable对象才能保存该索引。这个索引就是table(lua)在Lua注册表中的引用。LuaTable类中封装了Get方法,其原理也是通过压栈操作,获取类型对应的转换器函数,在栈上通过索引取到对应的独享,通过此方法就可以完整的访问table的内容了。

C#调用Lua function

    如何传递一个function给C#调用。首先需要声明一个委托类型(尽量使用封装好的Action,Func),并声明一个静态变量。

[LuaCallCSharp]
public class TestXlua
{
    [CSharpCallLua]
    //打标签是为了执行Generate Code后生成与委托参数匹配的成员方法,并且以"__Gen_Delegate_Imp"开头
    public delegate int Func(string s, bool b, int i);
    public static Func func;
}

//生成的方法 DelegatesGensBridge.cs
public int __Gen_Delegate_Imp11(string p0, bool p1, int p2)
{
#if THREAD_SAFE || HOTFIX_ENABLE
    lock (luaEnv.luaEnvLock)
    {
#endif
        RealStatePtr L = luaEnv.rawL;
        int errFunc = LuaAPI.pcall_prepare(L, errorFuncRef, luaReference);
        
        LuaAPI.lua_pushstring(L, p0);
        LuaAPI.lua_pushboolean(L, p1);
        LuaAPI.xlua_pushinteger(L, p2);
        
        PCall(L, 3, 1, errFunc);
 
        int __gen_ret = LuaAPI.xlua_tointeger(L, errFunc + 1);
        LuaAPI.lua_settop(L, errFunc - 1);
        return  __gen_ret;
#if THREAD_SAFE || HOTFIX_ENABLE
    }
#endif
}

    在将一个lua function赋值给TestXlua.func时CS.TestXlua.func = function(a,b,c) end也跟lua table一样,调用set包裹方法之前,把function压入栈,通过转换器函数拿到引用,赋值给func变量。其中不同的是,lua function会在C#侧创建一个委托,其过程可以简单理解为先创建一个DelegateBridge对象,这个对象保持着lua function的索引,然后通过委托的类型,在DelegatesGensBridge生成代码中,查找以__Gen_Delegate_Imp开头的与委托参数一致的方法,借此来创建一个同类型的委托,并绑定生成代码中相匹配的以__Gen_Delegate_Imp开头的方法。通过生成函数的内部实现可以看出,其主要功能是参数压栈和lua function的调用。

参考:
深入理解xlua基于IL代码注入的热更新原理
看懂xlua实现原理

你可能感兴趣的:(xlua,lua,c#,开发语言)