Lua元表配置:让SetSize修改父表宽高而非tbl实例属性
问题根因
- 调用
tbl:SetSize(200,200)时,Lua的冒号语法会自动把tbl本身作为self参数传入方法,方法内self.width = w的赋值操作会直接在tbl的原生表上新增width、height字段,这些字段属于tbl自身,因此pairs遍历tbl时会被打印出来。 - 原代码中
meta.__index指向的是base_table的深拷贝副本,而非base_table本身,就算修改属性也改动不到公共的base_table内容。 - Lua默认逻辑会将所有
tbl[k] = v的赋值直接存储在tbl自身的表结构中,没有额外拦截逻辑时,无法跳过自身表直接把值写入其他存储位置。 - 原
PrintTable函数存在逻辑bug:判断值是否为表时错误使用v == "table",实际应该判断值的类型,否则递归打印逻辑永远不会触发。
核心修改方案
- 取消对
base_table的深拷贝,直接将base_table作为元表__index的指向:当访问tbl不存在的属性时,会自动从base_table中读取对应值,保证属性访问正常。 - 为元表新增
__newindex拦截逻辑:当尝试对tbl自身不存在的键执行赋值操作时,不将键值对写入tbl本身,而是直接转发到base_table中对应键的位置,从根源上避免tbl自身新增任何字段。 - 修正
PrintTable的类型判断错误,保证递归打印逻辑正常运行。
修改后可运行的完整代码
function deepCopy(original) local copy = {} for k, v in pairs(original) do if type(v) == "table" then v = deepCopy(v) end copy[k] = v end return copy end function PrintTable(t) for k, v in pairs(t) do -- 修正原代码的类型判断错误 if type(v) == "table" then PrintTable(v) else print(k, v) end end end local base_table = { width = 0, height = 0, x = 0, y = 0, SetSize = function(self, w, h) self.width, self.height = w, h end, SetPos = function(self, x, y) self.x, self.y = x,y end } local tbl = {} -- 配置元表规则 local meta = { -- 直接引用原base_table,不做深拷贝,保证读取同一份存储 __index = base_table, -- 拦截所有对tbl的写入操作,直接转发到base_table __newindex = function(t, k, v) base_table[k] = v end } setmetatable(tbl, meta) PrintTable(tbl) -- 遍历tbl自身,无任何输出 tbl:SetSize(200, 200) PrintTable(tbl) -- 再次遍历tbl自身,依然无任何输出 -- 验证功能正常 print(tbl.width) -- 输出200 print(tbl.height) -- 输出200 print(base_table.width) -- 输出200,确认值直接写入到base_table中
运行效果说明
- 所有属性值、方法都存储在
base_table中,tbl自身的原生表永远为空,pairs只会遍历表自身的可枚举键,不会读取元表关联的__index内容,因此两次PrintTable(tbl)都不会有任何输出,完全符合需求。 - 调用
tbl:SetSize()时,赋值操作会被__newindex拦截,直接修改base_table的对应属性,不会在tbl自身留下任何字段。 - 访问
tbl.width、tbl.x这类属性时,因为tbl自身没有对应键,会自动触发__index逻辑从base_table中读取值,保证访问逻辑正常。
内容的提问来源于stack exchange,提问作者noobyy
相关产品推荐
相关产品推荐

