xLua
xLua copied to clipboard
util.state 实现方案的疑问
现在 util 文件中给 C# 对象新增字段的方案为 util.state,实现其实就是使用 Lua 的元表。 但是现在发现会有这样的问题:
-- 在某个地方加了一个新字段
util.state(csObj, {
lua_newTable1 = {}
})
-- 在另一个地方加了一个新字段
util.state(csObj, {
lua_newTable2 = {}
})
csObj.lua_newTable1.flag = true
-- 想要置空 lua_newTable1 这个字段
csObj.lua_newTable1 = nil
print('csObj.lua_newTable1:' .. tostring(csObj.lua_newTable1))
期望的结果是 csObj 中 lua_newTable1 这个字段被确实置空。 但是,实际结果是访问 csObj.lua_newTable1还是会有数据。 输出为:
LUA: csObj.lua_newTable1:table: 000001F0D9079CA0
原因是,util.state 的实现有把旧的 table 数据完全复制到新的 table 中
-- 现在的实现方案
local function state(csobj, state)
local csobj_mt = getmetatable(csobj)
-- 把上次的 state 表数据放到这次的 state 表中
for k, v in pairs(csobj_mt) do rawset(state, k, v) end
local csobj_index, csobj_newindex = state.__index, state.__newindex
state.__index = function(obj, k)
return rawget(state, k) or csobj_index(obj, k)
end
state.__newindex = function(obj, k, v)
if rawget(state, k) ~= nil then
rawset(state, k, v)
else
csobj_newindex(obj, k, v)
end
end
debug.setmetatable(csobj, state)
return state
end
这就导致,数据其实是有两份的,设置 csObj.lua_newTable1 = nil 其实只是把最上一层state表的 lua_newTable1 置空了,最开始那层state表的 lua_newTable1 还是存在的。 感觉比较合适的实现方案是这样的:
-- 多层嵌套 state 不是走复制数据,而是走元表,保证数据唯一
local function state(csobj, state)
local csobj_mt = getmetatable(csobj)
-- for k, v in pairs(csobj_mt) do rawset(state, k, v) end
local csobj_index, csobj_newindex = state.__index, state.__newindex
state.__index = function(obj, k)
return rawget(state, k) or (csobj_index and csobj_index(obj, k)) or csobj_mt[k]
end
state.__newindex = function(obj, k, v)
if rawget(state, k) ~= nil then
rawset(state, k, v)
elseif csobj_newindex then
csobj_newindex(obj, k, v)
else
csobj_mt[k] = v
end
end
debug.setmetatable(csobj, state)
return state
end
最后的写法有点问题,好像这样更合理
local function state(csobj, state)
local csobj_mt = getmetatable(csobj)
-- for k, v in pairs(csobj_mt) do rawset(state, k, v) end
local csobj_index, csobj_newindex = state.__index, state.__newindex
state.__index = function(obj, k)
-- 先看最上层走__index元方法,再看下一层
return rawget(state, k) or (csobj_index and csobj_index(obj, k)) or rawget(csobj_mt, k) or csobj_mt.__index(obj, k)
end
state.__newindex = function(obj, k, v)
-- 对应__index元方法的实现
if rawget(state, k) ~= nil then
rawset(state, k, v)
elseif csobj_newindex then
csobj_newindex(obj, k, v)
elseif rawget(csobj_mt, k) ~= nil then
rawset(csobj_mt, k, v)
else
csobj_mt.__newindex(obj, k, v)
end
end
debug.setmetatable(csobj, state)
return state
end