调用例子1
C#层用delegate接收lua层的函数时,需要把lua栈中的function转换为C#层的结构,最简单的调用就比如说从Global中获取函数。

--Test.lua testFunc1 = function() print("无参无返回") end -- c# CustomCall1 customCall1 = LuaManager.GetInstance().Global.Get<CustomCall1>("testFunc1");
调用例子2
xlua自动生成(Gen)的wrap,也就是接收Lua参数的地方,只能取到lua函数,而C#层用于接收这个函数的,大部分是delegate,但是delegate无法直接保存lua函数,就需要一些操作来转换下。考虑如下:
//原函数签名 public static uint PostEvent(uint in_eventID, UnityEngine.GameObject in_gameObjectID, uint in_uFlags, AkCallbackManager.EventCallback in_pfnCallback, object in_pCookie, uint in_cExternals, AkExternalSourceInfoArray in_pExternalSources, uint in_PlayingID) //lua调用 ak_sound_engine.PostEvent(id, go, 1, (self, "on_event_finish"), nil) //生成的wrap AkCallbackManager.EventCallback _in_pfnCallback = translator.GetDelegate<AkCallbackManager.EventCallback>(L, 4); uint gen_ret = AkSoundEngine.PostEvent( _in_eventID, _in_gameObjectID, _in_uFlags, _in_pfnCallback, _in_pCookie );
不难发现,通过translator.GetObject方法,将lua栈中传来的函数包装成委托发送给了实际的函数
public T GetDelegate<T>(RealStatePtr L, int index) where T :class { if (LuaAPI.lua_isfunction(L, index)) { return CreateDelegateBridge(L, typeof(T), index) as T; } else if (LuaAPI.lua_type(L, index) == LuaTypes.LUA_TUSERDATA) { return (T)SafeGetCSObj(L, index); } else { return null; } }
这里暂时只考虑传递过来的函数是lua函数的情况。初次访问的代码如下:
//简化版,初次访问 public object CreateDelegateBridge(RealStatePtr L, Type delegateType, int idx){ LuaAPI.lua_pushvalue(L, idx);//委托的那个lua函数 int reference = LuaAPI.luaL_ref(L);//xlua定义中,表t直接就是注册表了 //上面两行的作用是把表lua函数加入到注册表中,并且返回其在注册表中的索引 //上面两行是ref = 函数,后面三行是函数 = ref。 //也就是说既可以通过函数找ref,也可以通过ref找函数 LuaAPI.lua_pushvalue(L, idx); LuaAPI.lua_pushnumber(L, reference); LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX); DelegateBridgeBase bridge; bridge = new DelegateBridge(reference, luaEnv);//将lua函数的reference加入到bridge对象 var ret = getDelegate(bridge, delegateType); bridge.AddDelegate(delegateType, ret); delegate_bridges[reference] = new WeakReference(bridge); return ret; } Delegate getDelegate(DelegateBridgeBase bridge, Type delegateType) { Delegate ret = bridge.GetDelegateByType(delegateType); if (ret != null) { return ret; } } //DelegatesGensBridge.cs -- 自动生成的 public override Delegate GetDelegateByType(Type type) { if (type == typeof(FpUI.FpImageGuideMask.IsRaycastValid)) { return new FpUI.FpImageGuideMask.IsRaycastValid(__Gen_Delegate_Imp0); } return null; } public void AddDelegate(Type key, Delegate value) { if (key == firstKey) { throw new ArgumentException("An element with the same key already exists in the dictionary."); } if (firstKey == null && bindTo == null) // nothing { firstKey = key; firstValue = value; } else if (firstKey != null && bindTo == null) // one key existed { //如果一个lua函数对应多个委托 bindTo = new Dictionary<;Type, Delegate>;(); bindTo.Add(firstKey, firstValue); firstKey = null; firstValue = null; bindTo.Add(key, value); } else { bindTo.Add(key, value); } }
如上代码,前五行的LuaAPI将lua函数放入了注册表并且和ref一一对应,而后根据这个唯一对应lua函数的ref创建了bridge(也就是说,每一个bridge都一一对应了一个lua层的函数)
而后就可以根据委托的类型,根据自动生成的该委托实例的代码,生成一个委托(getDelegate(bridge, delegateType);) 然后将这个委托加入到bridge并且弱引用表指向它。
若之前根据这个相同的lua函数生成过bridge了,那就不需要再次生成了可以复用已有的bridge,代码如下:
public object CreateDelegateBridge(RealStatePtr L, Type delegateType, int idx) { LuaAPI.lua_pushvalue(L, idx); LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX); //根据函数找ref if (!LuaAPI.lua_isnil(L, -1)) //其实这里是判断之前有没有注册过这个委托 { int referenced = LuaAPI.xlua_tointeger(L, -1); LuaAPI.lua_pop(L, 1); if (delegate_bridges[referenced].IsAlive)//如果被回收了就当没有了 { if (delegateType == null) { return delegate_bridges[referenced].Target; } DelegateBridgeBase exist_bridge = delegate_bridges[referenced].Target as DelegateBridgeBase; Delegate exist_delegate; if (exist_bridge.TryGetDelegate(delegateType, out exist_delegate)) { return exist_delegate; } else { exist_delegate = getDelegate(exist_bridge, delegateType); exist_bridge.AddDelegate(delegateType, exist_delegate); return exist_delegate; } } } else { LuaAPI.lua_pop(L, 1);//弹出push的idx } }
从注册表查找,如果之前生成过bridge,则复用之前的。(如果弱引用被回收了,则还是走首次生成的逻辑。不过这里我有疑问,什么时候清的它的注册表?)然后通过类型,从这个bridge中获得这个类型的delegate,如果没有就新生成一个加入。
再来仔细看下自动生成的委托实例
public bool __Gen_Delegate_Imp0(UnityEngine.Vector2 p0) { #if THREAD_SAFE || HOTFIX_ENABLE lock (luaEnv.luaEnvLock) { #endif RealStatePtr L = luaEnv.rawL; int errFunc = LuaAPI.pcall_prepare(L, errorFuncRef, luaReference); ObjectTranslator translator = luaEnv.translator; translator.PushUnityEngineVector2(L, p0); PCall(L, 1, 1, errFunc); bool __gen_ret = LuaAPI.lua_toboolean(L, errFunc + 1); LuaAPI.lua_settop(L, errFunc - 1); return __gen_ret; #if THREAD_SAFE || HOTFIX_ENABLE } #endif } LUA_API int pcall_prepare(lua_State *L, int error_func_ref, int func_ref) { lua_rawgeti(L, LUA_REGISTRYINDEX, error_func_ref); lua_rawgeti(L, LUA_REGISTRYINDEX, func_ref); return lua_gettop(L) - 1; } public void PCall(IntPtr L, int nArgs, int nResults, int errFunc) { if (LuaAPI.lua_pcall(L, nArgs, nResults, errFunc) != 0) luaEnv.ThrowExceptionFromError(errFunc - 1); }
在这里,根据delegate中存储的ref找到了lua函数,并且通过注册表获得了这个函数并且放入了栈顶。并且后面压入了参数,使用PCall进行调用。LuaAPI.lua_toboolean(L, errFunc + 1);获取返回值,并用LuaAPI.lua_settop(L, errFunc – 1);清理栈