Код, который вы указали, является попыткой использовать шаблон таблицы прокси. Это работает, но это недопустимая реализация «таблиц только для чтения». Это потому, что ваша прокси-таблица содержит ссылку на таблицу, которую она должна покрыть Он хранится в поле с ключом, равным index
. Это означает, что можно легко редактировать значения, которые должны быть доступны только для чтения, например ::
local A = readonly {foo = 7}
print(A.foo) -- prints: 7
local _,ro = next(A)
ro.foo = 17
print(A.foo) -- prints: 17
Как должен работать "Proxy Table"? Короче говоря, вся идея состоит в том, чтобы использовать пустую таблицу в качестве прокси между пользователем и таблицей только для чтения. Мы назначаем метатаблицу с метаметодами __index
и __newindex
для таблицы прокси.
__index
вызывается всякий раз, когда кто-то пытается получить доступ к полю, которое «содержит» значение nil
.
__newindex
вызывается всякий раз, когда кто-то пытается создать новое поле в таблице.
Поскольку наша прокси-таблица всегда пуста, каждое назначение будет запускать __newindex
:
local B = readonly {bar = 8}
B.foo = 7 -- non-existent in both proxy and readonly table -> calls __newindex
B.bar = 3 -- exists in readonly table but does not exist in proxy -> calls __newindex
По той же причине, каждый раз, когда к полю обращаются __index
сработает:
local B = readonly {bar = 8}
print(B.foo) -- does not exist in proxy, __index is called -> prints "nil"
print(B.bar) -- does not exist in proxy, __index is called -> prints "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
В случае сомнений вы можете обратиться к: