唯讀表格

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"!

請參閱 GeneralizedPairsAndIpairs,瞭解相關應對方法。

原始作者:KevinBaca

另請參閱


RecentChanges · 首選設定
編輯 · 歷程記錄
上次編輯時間:2016 年 3 月 8 日下午 3:26 GMT (diff)