xLua已经默认在castersMap中为一些类型定义好了转换函数,其中就包括LuaTable类型
// ObjectCasters.cs public ObjectCasters(ObjectTranslator translator) { this.translator = translator; castersMap[typeof(char)] = charCaster; castersMap[typeof(sbyte)] = sbyteCaster; castersMap[typeof(byte)] = byteCaster; castersMap[typeof(short)] = shortCaster; castersMap[typeof(ushort)] = ushortCaster; castersMap[typeof(int)] = intCaster; castersMap[typeof(uint)] = uintCaster; castersMap[typeof(long)] = longCaster; castersMap[typeof(ulong)] = ulongCaster; castersMap[typeof(double)] = getDouble; castersMap[typeof(float)] = floatCaster; castersMap[typeof(decimal)] = decimalCaster; castersMap[typeof(bool)] = getBoolean; castersMap[typeof(string)] = getString; castersMap[typeof(object)] = getObject; castersMap[typeof(byte[])] = getBytes; castersMap[typeof(IntPtr)] = getIntptr; //special type castersMap[typeof(LuaTable)] = getLuaTable; castersMap[typeof(LuaFunction)] = getLuaFunction; }LuaTable对应的转换函数是getLuaTable
// ObjectCasters.cs private object getLuaTable(RealStatePtr L, int idx, object target) { if (LuaAPI.lua_type(L, idx) == LuaTypes.LUA_TUSERDATA) { object obj = translator.SafeGetCSObj(L, idx); return (obj != null && obj is LuaTable) ? obj : null; } if (!LuaAPI.lua_istable(L, idx)) { return null; } // 处理普通table类型 LuaAPI.lua_pushvalue(L, idx); return new LuaTable(LuaAPI.luaL_ref(L), translator.luaEnv); }getLuaTable的主要逻辑是将idx处的table通过luaL_ref添加到Lua注册表中并得到指向该table的索引,然后创建LuaTable对象保存该索引。也就是说Lua table在C#这边对应的是LuaTable对象,它们之间通过一个索引关联起来,这个索引表示Lua table在Lua注册表中的引用,利用这个索引可以获取到Lua table。拿到Lua table后,就可以继续访问Lua table的内容了。
// CS测试代码 int num = TestXLua.tab.Get<int>("num");对Lua table的访问操作都被封装在LuaTable的Get方法中
// LuaTable.cs public TValue Get<TValue>(string key) { TValue ret; Get(key, out ret); return ret; } // no boxing version get public void Get<TKey, TValue>(TKey key, out TValue value) { #if THREAD_SAFE || HOTFIX_ENABLE lock (luaEnv.luaEnvLock) { #endif var L = luaEnv.L; var translator = luaEnv.translator; int oldTop = LuaAPI.lua_gettop(L); LuaAPI.lua_getref(L, luaReference); // 通过luaReference获取到对应的table translator.PushByType(L, key); if (0 != LuaAPI.xlua_pgettable(L, -2)) // 查询 表[key] { string err = LuaAPI.lua_tostring(L, -1); LuaAPI.lua_settop(L, oldTop); throw new Exception("get field [" + key + "] error:" + err); } LuaTypes lua_type = LuaAPI.lua_type(L, -1); Type type_of_value = typeof(TValue); if (lua_type == LuaTypes.LUA_TNIL && type_of_value.IsValueType()) { throw new InvalidCastException("can not assign nil to " + type_of_value.GetFriendlyName()); } try { translator.Get(L, -1, out value); // 获取栈顶的元素,即 表[key] } catch (Exception e) { throw e; } finally { LuaAPI.lua_settop(L, oldTop); } #if THREAD_SAFE || HOTFIX_ENABLE } #endif }Get方法的主要逻辑是,先通过保存的索引luaReference获取到Lua table,然后通过xlua_pgettable将 表[key] 的值压栈,最后通过translator.Get获取到栈顶值对应的对象
// ObjectTranslator.cs public void Get<T>(RealStatePtr L, int index, out T v) { Func<RealStatePtr, int, T> get_func; if (tryGetGetFuncByType(typeof(T), out get_func)) { v = get_func(L, index); // 将给定索引处的值转换为{T}类型 } else { v = (T)GetObject(L, index, typeof(T)); } }同样的,xLua也在tryGetGetFuncByType中为一些基本类型预定义好了对应的对象获取方法,采取泛型方式,这样可以避免拆箱和装箱。在本例中获取的值 num = 1 是一个int类型,通过转换器函数xlua_tointeger即可获得。xlua_tointeger就是对Lua原生API lua_tointeger的一个简单封装
bool tryGetGetFuncByType<T>(Type type, out T func) where T : class { if (get_func_with_type == null) { get_func_with_type = new Dictionary<Type, Delegate>() { {typeof(int), new Func<RealStatePtr, int, int>(LuaAPI.xlua_tointeger) }, {typeof(double), new Func<RealStatePtr, int, double>(LuaAPI.lua_tonumber) }, {typeof(string), new Func<RealStatePtr, int, string>(LuaAPI.lua_tostring) }, {typeof(byte[]), new Func<RealStatePtr, int, byte[]>(LuaAPI.lua_tobytes) }, {typeof(bool), new Func<RealStatePtr, int, bool>(LuaAPI.lua_toboolean) }, {typeof(long), new Func<RealStatePtr, int, long>(LuaAPI.lua_toint64) }, {typeof(ulong), new Func<RealStatePtr, int, ulong>(LuaAPI.lua_touint64) }, {typeof(IntPtr), new Func<RealStatePtr, int, IntPtr>(LuaAPI.lua_touserdata) }, {typeof(decimal), new Func<RealStatePtr, int, decimal>((L, idx) => { decimal ret; Get(L, idx, out ret); return ret; }) }, {typeof(byte), new Func<RealStatePtr, int, byte>((L, idx) => (byte)LuaAPI.xlua_tointeger(L, idx) ) }, {typeof(sbyte), new Func<RealStatePtr, int, sbyte>((L, idx) => (sbyte)LuaAPI.xlua_tointeger(L, idx) ) }, {typeof(char), new Func<RealStatePtr, int, char>((L, idx) => (char)LuaAPI.xlua_tointeger(L, idx) ) }, {typeof(short), new Func<RealStatePtr, int, short>((L, idx) => (short)LuaAPI.xlua_tointeger(L, idx) ) }, {typeof(ushort), new Func<RealStatePtr, int, ushort>((L, idx) => (ushort)LuaAPI.xlua_tointeger(L, idx) ) }, {typeof(uint), new Func<RealStatePtr, int, uint>(LuaAPI.xlua_touint) }, {typeof(float), new Func<RealStatePtr, int, float>((L, idx) => (float)LuaAPI.lua_tonumber(L, idx) ) }, }; } 传递Lua function到C#