lua设置和创建只读table
lua中设置和创建只读table
首先感谢 Lua 设置table为只读属性
在被人的肩膀上略微做了些修改
使用到的 lua metamethod
-
__index
当你通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。如果__index包含一个表格,Lua会在表格中查找相应的键。 -
__newindex
当你给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:如果存在则调用这个函数而不进行赋值操作。
__newindex 元方法对表更新操作
代码实现
-** 创建只读 table
-- 允许插入添加新的数据,允许访问已有的数据,当修改以后的数据时会提示error
-- 每个tb只创建一个 proxy, 避免重复设置 read_only
-- travelled_tables 避免表间互相引用
-- proxy 是返回的 read_only_table, 其永远保持空表,使得每次赋值都可以调用__newindex
-- lua5.3支持 table.remove 抛出错误,
-- 支持 pairs ipairs 迭代遍历, 不支持 next访问
-- 用法:
-- local tb = CreateReadonlyTable()
-- tb.a = 1
-- table.insert(tb, 2)
-- table.tt = {a = 1}
*]]
local function CreateReadonlyTable()
local readonly_table = {}
local travelled_tables = {}
local function __read_only(tb)
if travelled_tables[tb] then
return travelled_tables[tb]
end
local tb_mt = getmetatable(tb)
if tb_mt == nil then
tb_mt = {}
setmetatable(tb, tb_mt)
end
local proxy = tb_mt.__read_only_proxy
if not proxy then
proxy = {}
tb_mt.__read_only_proxy = proxy
local proxy_mt = {}
proxy_mt.__index = tb
proxy_mt.__newindex = function(t, key, value)
local gain_value = rawget(tb, key)
if gain_value ~= nil then
error("error write to a read-only table with key =".. tostring(k))
return
end
if type(value) == "table" then
rawset(tb, key, __read_only(value))
else
rawset(tb, key, value)
end
end
proxy_mt.__pairs = function(t)
return pairs(tb)
end
proxy_mt.__ipairs = function(t)
return ipairs(tb)
end
proxy_mt.__len = function(t)
return #tb
end
proxy_mt.__read_only_proxy = proxy
setmetatable(proxy, proxy_mt)
end
travelled_tables[tb] = proxy
return proxy
end
return __read_only(readonly_table)
end
- 创建并返回空表 proxy, 重写 __newindex 使其一直保持空表形式。使得赋值操作都会触发其 __newindex元方法。新插入的数据被添加到readonly_table表中
- __index 从readonly_table 表中取出已有数据
- __read_only_proxy 是为避免重复的read_only 设置
- travelled_tables 为避免表之间的互相引用
在贴上大牛的原创
--[[------------------------------------------------------------------------------
-** 设置table只读
-- 出现改写会抛出lua error
-- 用法 local cfg_proxy = read_only(cfg) retur cfg_proxy
-- 增加了防重置设置read_only的机制
-- lua5.3支持 1)table库支持调用元方法,所以table.remove table.insert 也会抛出错误,
-- 2)不用定义__ipairs 5.3 ipairs迭代器支持访问元方法__index,pairs迭代器next不支持故需要元方法__pairs
-- 低版本lua此函数不能完全按照预期工作
*]]
function read_only(inputTable)
local travelled_tables = {}
local function __read_only(tbl)
if not travelled_tables[tbl] then
local tbl_mt = getmetatable(tbl)
if not tbl_mt then
tbl_mt = {}
setmetatable(tbl, tbl_mt)
end
local proxy = tbl_mt.__read_only_proxy
if not proxy then
proxy = {}
tbl_mt.__read_only_proxy = proxy
local proxy_mt = {
__index = tbl,
__newindex = function (t, k, v) error("error write to a read-only table with key = " .. tostring(k)) end,
__pairs = function (t) return pairs(tbl) end,
-- __ipairs = function (t) return ipairs(tbl) end, 5.3版本不需要此方法
__len = function (t) return #tbl end,
__read_only_proxy = proxy
}
setmetatable(proxy, proxy_mt)
end
travelled_tables[tbl] = proxy
for k, v in pairs(tbl) do
if type(v) == "table" then
tbl[k] = __read_only(v)
end
end
end
return travelled_tables[tbl]
end
return __read_only(inputTable)
end