只读表

lua-users home
wiki

本文介绍了一种通过 Lua 5.0/5.1 中的元方法使表只读的技术(有关元方法的背景信息,请参阅 元方法教程)。这种方法只能够防止对只读表中任何成员的意外修改。

我们可以像这样定义只读(常量)表

Directions = readonlytable {
  LEFT   = 1,
  RIGHT  = 2,
  UP     = 3,
  DOWN   = 4,
  otherstuff = {}
}

如果我们像下面这样定义 readonlytable 辅助函数

function readonlytable(table)
   return setmetatable({}, {
     __index = table,
     __newindex = function(table, key, value)
                    error("Attempt to modify read-only table")
                  end,
     __metatable = false
   });
end

注意,readonlytable 不会返回最初传递给它的表,而是返回一个代理表。代理表被赋予一个元表,其中包含元方法 __index__newindex,以确保代理表的值永远不会改变。设置 __metatable 元方法可以防止对元表本身进行篡改——客户端无法通过 getmetatablesetmetatable 函数获取或更改元表。

现在,如果我们尝试修改 Directions 的任何成员,我们将得到一个错误。

> Directions.LEFT = 33
Attempt to modify read-only table

虽然无法更改只读表的成员,但仍然可以修改只读表成员的成员(除非它们也被明确地设置为只读表)。

> Directions.otherstuff = nil    -- will fail
Attempt to modify read-only table
> Directions.otherstuff.foo = 1  -- allowed

此外,rawset()table.insert 仍然可以用来直接修改只读表。

rawset(Directions, "LEFT", 5)
print(Directions.LEFT)         -- prints 5
table.insert(Directions, 6)
print(Directions[1])           -- prints 6

如果你真的需要避免这种情况,你可以在 C 中实现只读表。

此外,这种创建只读表的方法会干扰 pairsipairsnext# 运算符和其他形式的表迭代。例如,

-- prints nothing!
for k,v in pairs(Directions) do
  print(k,v)
end

print(next(Directions))  -- prints nil!
print(#Directions)       -- prints "0"!

另请参阅 广义的 Pairs 和 Ipairs,了解处理这种情况的方法。

原作者:KevinBaca

另请参阅


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2016 年 3 月 8 日下午 3:26 GMT (差异)