Lua中設(shè)置table為只讀屬性的方法詳解
項目中部分只讀表易被人誤改寫,故決定在非線上環(huán)境里對這些表附加只讀屬性,方便在出現(xiàn)誤改寫的時候拋出lua錯誤,最終版代碼如下:
--[[------------------------------------------------------------------------------ -** 設(shè)置table只讀 出現(xiàn)改寫會拋出lua error -- 用法 local cfg_proxy = read_only(cfg) retur cfg_proxy -- 增加了防重置設(shè)置read_only的機(jī)制 -- lua5.3支持 1)table庫支持調(diào)用元方法,所以table.remove table.insert 也會拋出錯誤, -- 2)不用定義__ipairs 5.3 ipairs迭代器支持訪問元方法__index,pairs迭代器next不支持故需要元方法__pairs -- 低版本lua此函數(shù)不能完全按照預(yù)期工作 *]] 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)
測試環(huán)境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
代碼思路設(shè)計:
1.使用proxy={}
空表而不是目標(biāo)表tbl來設(shè)置__newindex是因?yàn)開_newindex必須在原表里面不存在才會調(diào)用,這樣就依然可以對已存在的字段進(jìn)行改寫
__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.避免出現(xiàn)table的互相引用,加入travelled_tables存儲已經(jīng)設(shè)置過proxy的table的映射
3.對于原表tbl的訪問使用__index=tbl
4.對于表查長度使用__len= function () return #tbl end
5.對于遍歷pairs,查到lua5.3的pairs默認(rèn)迭代器next不支持訪問元表__index,故直接__pairs = function () return pairs(tbl) end
,以此來生成對目標(biāo)表的迭代遍歷
6.對于ipairs,查到lua5.3 ipairs函數(shù)生成的迭代器默認(rèn)就支持訪問元表__index,故不需要添加__ipairs
8.2 – Changes in the Libraries
•The ipairs iterator now respects metamethods and its __ipairs metamethod has been deprecated.
7.對于table.insert
, table.remove
不用特殊處理,lua5.3的table lib支持元表操作,故依然會拋錯
8.2 – Changes in the Libraries
•The Table library now respects metamethods for setting and getting elements.
8.避免重復(fù)創(chuàng)建read_only,每個tbl只創(chuàng)建一個proxy代理,在tbl的metatable里和proxy的metatable里都設(shè)置屬性__read_only_proxy,可以直接訪問獲得
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對本站的支持。
版權(quán)聲明:本站文章來源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請保持原文完整并注明來源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學(xué)習(xí)參考,不代表本站立場,如有內(nèi)容涉嫌侵權(quán),請聯(lián)系alex-e#qq.com處理。