为什么这个代码意味着所有表共享一个元表

我得到了这样的一个要求:所有只读表可以共享一个元表,这段代码回答了这个要求,但我不理解这段代码是如何实现这个目标的

local index = {}
local mt = {
  __index = function ( t, k )
    return t[index][k]
  end,

  __newindex = function ( t, k, v )
    -- body
    error("更新值是被禁止的",2)
  end
}

function readonly(t)
  local proxy = {}
  proxy[index] = t
  setmetatable(proxy,mt)
  return proxy
end
点赞
用户4567755
用户4567755

你提供的代码似乎是试图使用代理表模式,它可以工作,但不是“只读表”的有效实现。这是因为您的代理表保持对其所涵盖的表的引用。它存储在键等于index的字段中。这意味着可以很容易地使用以下方法编辑应该是只读的值:

local A = readonly {foo = 7}
print(A.foo) -- 输出:7
local _,ro = next(A)
ro.foo = 17
print(A.foo) -- 输出:17

“代理表”应该如何工作?简单来说,整个想法是使用一个空表作为用户和只读表之间的代理。我们将一个带有__index__newindex元方法的元表分配给代理表。

  • 当尝试访问“持有”值nil的字段时,将调用__index
  • 当尝试在表中创建新字段时,将调用__newindex

由于我们的代理表总是空的,每次赋值都会触发__newindex

local B = readonly {bar = 8}
B.foo = 7 -- 既不在代理表中,也不在只读表中 -> 调用__newindex
B.bar = 3 -- 在只读表中存在但不存在于代理表中 -> 调用__newindex

同样的原因,每次访问字段时,__index都会被触发:

local B = readonly {bar = 8}
print(B.foo) -- 在代理表中不存在,调用__index -> 输出“nil”
print(B.bar) -- 在代理表中不存在,调用__index -> 输出“8”

至于更有效的示例,请参见下文。它仍然存在问题(例如,可以更改表模式以使键弱;请参见注释),但至少它涵盖了只读表。

local index = {}

local mt = {
  __index = function (t, k)
    return index[t][k]
  end,
  __newindex = function ()
    -- body
    error("update the value is prohibited",2)
  end,
}

function readonly (t)
  local proxy = {}
  index[proxy] = t
  setmetatable(proxy, mt)
  return proxy
end

如果有疑问,可以参考以下内容:

2019-07-08 12:48:05