lua中metatable的功能还是很强大的,本例展示一个lua的readonly table(参考《Lua程序设计(第二版)》中文版 13.4.5 只读的table)
-- 修改只读表格的错误处理函数
function error_readonly_handler(t, k, v)
error("attempt to update a read-only table", 2)
end
-- 将表格t的顶层包装为只读表格(非递归只读)
function readonly_table(t)
local proxy = {}
setmetatable(proxy, {__index = t, __newindex = error_readonly_handler});
return proxy;
end
-- 向给定的表格中插入一跳只读的记录并进行必要的key合法性检测
function table_add_readonly_row(t, r, key)
local row = readonly_table(r)
if row == nil then error("row can not be nil", 2) end
if key == nil then
t[#t + 1] = row
else
if row[key] == nil then error("row[key] can not be nil") end
if t[row[key]] ~= nil then error("t[row[key]] already exists") end
t[row[key]] = row
end
end
-- 将数据包装为只读表格的示例
function LoadTable_Data()
local proxy = {}
local index = "ID"
table_add_readonly_row(proxy, {["ID"] = 10010, ["Name"] = "Lucy"}, index)
-- ...
table_add_readonly_row(proxy, {["ID"] = 10011, ["Name"] = "Jack"}, index)
return readonly_table(proxy)
end
注意下面的写法是没有用的,可以正常执行,但t仍然是可以修改的。因为__index只有当表格中找不到元素时才会触发,所以必须通过一个空的代理表格进行中转包装。
setmetatable(t, {__index = t, __newindex = error_readonly_handler});
lua 5.2中支持table的__len metatable重写,可将以上函数设入长度操作符元表,这样就可通过‘#’很自然的得到表的真实长度了。--- @brief 得到表大小,对稀疏表、索引元表为表的表也适用function table_size(t)if t == nil then return 0 end-- 计算稀疏表的大小local n = 0local index = next(t)while index ~= nil don = n + 1index = next(t, index)end-- 计算代理索引表的大小local meta = getmetatable(t)if meta ~= nil thenlocal h = meta.__indexif type(h) == "table" thenn = n + table_size(h)endendreturn nend
评论