Content

Lua中设置table为只读属性的方法详解(lua中table.insert)越早知道越好

文章摘要

这篇文章介绍了一个在Lua中实现表的只读属性的技术,通过在非线上环境中为表附加`read_only`属性,当发生修改操作时会抛出Lua错误,从而防止数据意外修改。实现的核心是通过元方法和代理表技术,同时考虑了Lua 5.3及以上版本表库的支持特性。文章详细描述了实现逻辑、测试方法以及如何避免常见错误(如重复创建代理、处理pairs和ipairs迭代器的差异等)。总结了代码的核心逻辑,包括如何利用`__read_only`元方法、`travelled_tables`缓存机制以及如何确保在不同版本的Lua中兼容运行。文章还提到了改进措施,如简化代理表创建、利用Lua的特性优化性能。



项目中部分只读表易被人误改写,故决定在非线上环境里对这些表附加只读属性,方便在出现误改写的时候抛出lua错误,最终版代码如下:

–[[—————————————————————————— -** 设置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

测试代码如下:

local t0={k=1} local t2={ fdsf={456} } local t1={ a={456, 89}, b={456,ddss=9, t2=t2}, d=45, e=”string”, } t1.c=t1 local t3=read_only(t1) print(t3.d, t3.c.e, t3.c.c.b.t2.fdsf) function q1() t3.d=4555 end function q2() t3.c.d=90 end function q3() t3.c.c.b.t2.fdsf=90 end function q4() table.remove(t3.a) end function q5() t3.b[ddss]=nil end function q6() t3[f]=89 end function q7() table.insert(t3.a, 999) end print(pcall(q1)) print(pcall(q2)) print(pcall(q3)) print(pcall(q4)) print(pcall(q5)) print(pcall(q6)) print(pcall(q7)) print(t3.a[1]) for k,v in pairs(t3) do print(“===pairs t3:”,k,v) end for k,v in pairs(t3.a) do print(“===pairs t3.a:”,k,v) end for k,v in ipairs(t3) do print(“===ipairs t3:”,k,v) end for k,v in ipairs(t3.a) do print(“===ipair t3.a”,k,v) end print(“len t3:”,#t3) print(“len t3.a:”, #t3.a) local t4=read_only(t2) local t5=read_only(t0) local t6=read_only(t0) print(t3.b.t2, read_only(t2)) print(t5, t6, t0)

测试环境https://www.lua.org/cgi-bin/demo  lua5.3.4:

string table: 0x20d4ba0 false input:17: error write to a read-only table with key=d false input:17: error write to a read-only table with key=d false input:17: error write to a read-only table with key=fdsf false input:17: error write to a read-only table with key=2 false input:17: error write to a read-only table with key=nil false input:17: error write to a read-only table with key=nil false input:17: error write to a read-only table with key=3===pairs t3: e string===pairs t3: b table: 0x20ccd60===pairs t3: a table: 0x20d4e70===pairs t3: d 45===pairs t3: c table: 0x20ca700===pairs t3.a: 1 456===pairs t3.a: 2 89===ipair t3.a 1 456===ipair t3.a 2 89 len t3: 0 len t3.a: 2 table: 0x20d4870 table: 0x20d4870 table: 0x20d5690 table: 0x20d5690 table: 0x20d1140

代码思路设计:

1.使用空表而不是目标表tbl来设置__newindex是因为__newindex必须在原表里面不存在才会调用,这样就依然可以对已存在的字段进行改写

__newindex: The indexing assignment table[key]=value. Like the index event, this event happens when table is not a table or when key is not present in table. The metamethod is looked up in table. Like with indexing, the metamethod for this event can be either a function or a table. If it is a function, it is called with table, key, and value as arguments. If it is a table, Lua does an indexing assignment to this table with the same key and value. (This assignment is regular, not raw, and therefore can trigger another metamethod.) Whenever there is a __newindex metamethod, Lua does not perform the primitive assignment. (If necessary, the metamethod itself can call rawset to do the assignment.)

2.避免出现table的互相引用,加入travelled_tables存储已经设置过proxy的table的映射

3.对于原表tbl的访问使用

4.对于表查长度使用

5.对于遍历pairs,查到lua5.3的pairs默认迭代器next不支持访问元表__index,故直接,以此来生成对目标表的迭代遍历

6.对于ipairs,查到lua5.3 ipairs函数生成的迭代器默认就支持访问元表__index,故不需要添加__ipairs

     8.2 – Changes in the Libraries

     •The ipairs iterator now respects metamethods and its __ipairs metamethod has been deprecated.

7.对于, 不用特殊处理,lua5.3的table lib支持元表操作,故依然会抛错

      8.2 – Changes in the Libraries

      •The Table library now respects metamethods for setting and getting elements.

8.避免重复创建read_only,每个tbl只创建一个proxy代理,在tbl的metatable里和proxy的metatable里都设置属性__read_only_proxy,可以直接访问获得

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

您可能感兴趣的文章:深入谈谈lua中神奇的tableLua Table转C# Dictionary的方法示例Lua编程示例(一):select、debug、可变参数、table操作、error举例讲解Lua中的Table数据结构Lua table中安全移除元素的方法Lua的table库函数insert、remove、concat、sort详细介绍C++遍历Lua table的方法实例Lua中释放table占用内存的方法Lua中table的遍历详解Lua中获取table长度问题探讨Lua中获取table长度的方法Lua中table里内嵌table的例子Lua面向对象编程之基础结构table简例