Lua中为表特定键添加get/set函数的正确实现方法
Lua表自定义get/set时的无限递归问题解决
问题场景
我有一个包含函数、变量的Lua表,想给特定键(比如"b")添加自定义get/set逻辑:读取"b"时执行getb(),写入时执行setb()。但写的代码出现了无限递归,尤其是操作不存在的键(比如"d")时。
原代码:
t = { a = function () print("doa") end, c = function () print("doc") end } _b = 0 function getb() return _b end function setb(v) _b = v end mt = { __index = function (t,k) if (k == "b") then return getb() end -- 给特定键调用自定义get函数 return t[k] -- 访问原表 end, __newindex = function (t,k,v) if (k == "b") then setb(v) -- 给特定键调用自定义set函数 else t[k] = v end -- 访问原表 end } setmetatable(t, mt) t.a() t.b = 3 print(t.b) t.c() t.d = 3 -- 这行触发无限递归 print(t.d) -- 这行触发无限递归
问题原因
- __index的无限递归:当访问
t[k]且k不在t中时,会触发元表的__index方法。而代码中__index里又写了return t[k],这会再次触发__index,形成无限循环。 - __newindex的无限递归:当给t中不存在的键赋值时,会触发
__newindex方法。代码中__newindex里写了t[k] = v,这又会再次触发__newindex,导致无限递归。
解决方法
方法一:用rawget/rawset绕过元表
使用Lua原生的rawget和rawset函数,它们可以直接操作原始表,不会触发元表的__index或__newindex方法:
t = { a = function () print("doa") end, c = function () print("doc") end } _b = 0 function getb() return _b end function setb(v) _b = v end mt = { __index = function (t,k) if k == "b" then return getb() end return rawget(t, k) -- 直接读取原表,不触发__index end, __newindex = function (t,k,v) if k == "b" then setb(v) else rawset(t, k, v) -- 直接写入原表,不触发__newindex end end } setmetatable(t, mt) t.a() t.b = 3 print(t.b) --> 输出3 t.c() t.d = 3 print(t.d) --> 输出3
方法二:代理表+私有原始表
把原始表存为私有变量,对外暴露代理表,所有操作通过代理表转发,避免直接操作触发元表逻辑:
local _t = { a = function () print("doa") end, c = function () print("doc") end } local _b = 0 function getb() return _b end function setb(v) _b = v end local t = {} mt = { __index = function (_,k) if k == "b" then return getb() end return _t[k] end, __newindex = function (_,k,v) if k == "b" then setb(v) else _t[k] = v end end } setmetatable(t, mt) t.a() t.b = 3 print(t.b) --> 输出3 t.c() t.d = 3 print(t.d) --> 输出3
内容的提问来源于stack exchange,提问作者user1806687
相关产品推荐
相关产品推荐

