почему этот код означает, что все таблицы совместно используют один метатабельный - PullRequest
1 голос
/ 08 июля 2019

Я получил требование, чтобы все таблицы только для чтения могли совместно использовать одну метатабельность, кодовая фраза отвечает этому требованию, но я не могу понять, как эта кодовая фраза достигает этой цели

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

  __newindex = function ( t, k, v )
    -- body
    error("update the value is prohibited",2)
  end
}



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

1 Ответ

1 голос
/ 08 июля 2019

Код, который вы указали, является попыткой использовать шаблон таблицы прокси. Это работает, но это недопустимая реализация «таблиц только для чтения». Это потому, что ваша прокси-таблица содержит ссылку на таблицу, которую она должна покрыть Он хранится в поле с ключом, равным 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

В случае сомнений вы можете обратиться к:

...