若是轉載,請註明博文來源:http://www.cnblogs.com/vanishfan/p/6909153.html 望各位支持!html
項目中部分只讀表易被人誤改寫,故決定在非線上環境裏對這些表附加只讀屬性,方便在出現誤改寫的時候拋出lua錯誤,最終版代碼以下:app
1 --[[------------------------------------------------------------------------------ 2 -** 設置table只讀 出現改寫會拋出lua error 3 -- 用法 local cfg_proxy = read_only(cfg) retur cfg_proxy 4 -- 增長了防重置設置read_only的機制 5 -- lua5.3支持 1)table庫支持調用元方法,因此table.remove table.insert 也會拋出錯誤, 6 -- 2)不用定義__ipairs 5.3 ipairs迭代器支持訪問元方法__index,pairs迭代器next不支持故須要元方法__pairs 7 -- 低版本lua此函數不能徹底按照預期工做 8 *]] 9 function read_only(inputTable) 10 local travelled_tables = {} 11 local function __read_only(tbl) 12 if not travelled_tables[tbl] then 13 local tbl_mt = getmetatable(tbl) 14 if not tbl_mt then 15 tbl_mt = {} 16 setmetatable(tbl, tbl_mt) 17 end 18 19 local proxy = tbl_mt.__read_only_proxy 20 if not proxy then 21 proxy = {} 22 tbl_mt.__read_only_proxy = proxy 23 local proxy_mt = { 24 __index = tbl, 25 __newindex = function (t, k, v) error("error write to a read-only table with key = " .. tostring(k)) end, 26 __pairs = function (t) return pairs(tbl) end, 27 -- __ipairs = function (t) return ipairs(tbl) end, 5.3版本不須要此方法 28 __len = function (t) return #tbl end, 29 __read_only_proxy = proxy 30 } 31 setmetatable(proxy, proxy_mt) 32 end 33 travelled_tables[tbl] = proxy 34 for k, v in pairs(tbl) do 35 if type(v) == "table" then 36 tbl[k] = __read_only(v) 37 end 38 end 39 end 40 return travelled_tables[tbl] 41 end 42 return __read_only(inputTable) 43 end
測試代碼以下:函數
1 local t0 = {k = 1} 2 local t2 = { 3 fdsf = {456} 4 } 5 local t1 = { 6 a = {456, 89}, 7 b = {456,ddss = 9, t2 = t2}, 8 d = 45, 9 e = "string", 10 } 11 t1.c=t1 12 13 local t3 = read_only(t1) 14 15 print(t3.d, t3.c.e, t3.c.c.b.t2.fdsf) 16 function q1() t3.d = 4555 end 17 function q2() t3.c.d = 90 end 18 function q3() t3.c.c.b.t2.fdsf =90 end 19 function q4() table.remove(t3.a) end 20 function q5() t3.b[ddss] = nil end 21 function q6() t3[f] = 89 end 22 function q7() table.insert(t3.a, 999) end 23 24 print(pcall(q1)) 25 print(pcall(q2)) 26 print(pcall(q3)) 27 print(pcall(q4)) 28 print(pcall(q5)) 29 print(pcall(q6)) 30 print(pcall(q7)) 31 print(t3.a[1]) 32 for k,v in pairs(t3) do 33 print("===pairs t3:",k,v) 34 end 35 for k,v in pairs(t3.a) do 36 print("===pairs t3.a:",k,v) 37 end 38 for k,v in ipairs(t3) do 39 print("===ipairs t3:",k,v) 40 end 41 for k,v in ipairs(t3.a) do 42 print("===ipair t3.a",k,v) 43 end 44 print("len t3:",#t3) 45 print("len t3.a:", #t3.a) 46 47 local t4 = read_only(t2) 48 49 local t5 = read_only(t0) 50 local t6 = read_only(t0) 51 52 print(t3.b.t2, read_only(t2)) 53 print(t5, t6, t0)
測試環境https://www.lua.org/cgi-bin/demo lua5.3.4:測試
45 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 456 ===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
代碼思路設計:this
1.使用proxy={}空表而不是目標表tbl來設置__newindex是由於__newindex必須在原表裏面不存在纔會調用,這樣就依然能夠對已存在的字段進行改寫lua
__newindex
: The indexing assignmenttable[key] = value
. Like the index event, this event happens whentable
is not a table or whenkey
is not present intable
. The metamethod is looked up intable
.spaLike 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
, andvalue
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 callrawset
to do the assignment.)代理
2.避免出現table的互相引用,加入travelled_tables存儲已經設置過proxy的table的映射code
3.對於原表tbl的訪問使用__index=tbl
4.對於表查長度使用__len= function () return #tbl end
5.對於遍歷pairs,查到lua5.3的pairs默認迭代器next不支持訪問元表__index,故直接__pairs = function () return pairs(tbl) end,以此來生成對目標表的迭代遍歷
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.對於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.避免重複建立read_only,每一個tbl只建立一個proxy代理,在tbl的metatable裏和proxy的metatable裏都設置屬性__read_only_proxy,能夠直接訪問得到