Метаметод __newindex
Часть раздела Logic & Flow путешествия по Lua на Coddy — урок 19 из 54.
Вы узнали, как __index управляет тем, что происходит при попытке чтения ключа, которого не существует в таблице. Теперь вы познакомитесь с его аналогом: __newindex, который управляет тем, что происходит при попытке добавить или изменить пару «ключ-значение».
Метаметод __newindex срабатывает всякий раз, когда вы пытаетесь присвоить значение ключу, который в данный момент не существует в таблице. Это дает вам возможность перехватывать и контролировать изменения до того, как они произойдут.
Вот простой пример, который предотвращает добавление новых ключей в таблицу:
local readOnly = {}
local meta = {
__newindex = function(table, key, value)
print("Cannot add key: " .. key)
end
}
setmetatable(readOnly, meta)
readOnly.name = "Alice" -- Вывод: Cannot add key: name
print(readOnly.name) -- Вывод: nilКогда вы пытаетесь установить readOnly.name = "Alice", Lua видит, что name не существует в таблице. Вместо того чтобы добавить его, Lua вызывает функцию __newindex с тремя аргументами: саму таблицу, устанавливаемый ключ и присваиваемое значение.
В этом примере функция просто выводит сообщение и на самом деле ничего не сохраняет.
Вы также можете использовать __newindex для валидации данных перед их добавлением в таблицу:
local player = {}
local meta = {
__newindex = function(t, key, value)
if key == "health" and type(value) ~= "number" then
print("Health must be a number!")
else
rawset(t, key, value)
end
end
}
setmetatable(player, meta)
player.health = "high" -- Вывод: Health must be a number!
player.health = 100 -- РазрешеноОбратите внимание на использование rawset() во втором примере. Эта функция обходит метатаблицу и напрямую устанавливает значение в таблице.
Без этого установка значения снова вызовет __newindex, создавая бесконечный цикл.
Задание
ЛегкоНапишите функцию createValidatedTable, которая принимает key и value и возвращает таблицу, которая принимает только строковые значения для новых ключей.
Создайте пустую таблицу и прикрепите метатаблицу с __newindex, которая проверяет тип значения. Если кто-то попытается добавить нестроковое значение, используйте rawset(), чтобы вместо этого сохранить строку "invalid". Для строковых значений сохраняйте их обычным образом с помощью rawset().
Логика:
- Создайте пустую таблицу
- Создайте метатаблицу с
__newindex, которая получает таблицу, ключ и значение - Внутри
__newindexпроверьте, является ли значение строкой, используяtype(value) == "string" - Если это строка, используйте
rawset(t, key, value)для её сохранения - Если это не строка, используйте
rawset(t, key, "invalid"), чтобы вместо этого сохранить"invalid" - Прикрепите метатаблицу к таблице с помощью
setmetatable() - Используйте
rawset(), чтобы добавить начальную пару ключ-значение в таблицу (в обход метатаблицы) - Верните таблицу
Параметры:
key(string): Начальный ключ для добавления в таблицуvalue(string): Начальное значение для добавления в таблицу
Возвращает: Таблицу с валидацией, которая принимает только строковые значения, содержащую начальную пару ключ-значение (table)
Шпаргалка
Метаметод __newindex срабатывает, когда вы пытаетесь присвоить значение ключу, которого не существует в таблице, что позволяет вам перехватывать и контролировать изменения.
Базовый пример предотвращения создания новых ключей:
local readOnly = {}
local meta = {
__newindex = function(table, key, value)
print("Cannot add key: " .. key)
end
}
setmetatable(readOnly, meta)
readOnly.name = "Alice" -- Вывод: Cannot add key: name
print(readOnly.name) -- Вывод: nilФункция __newindex принимает три аргумента: саму таблицу, устанавливаемый ключ и присваиваемое значение.
Валидация данных с помощью __newindex:
local player = {}
local meta = {
__newindex = function(t, key, value)
if key == "health" and type(value) ~= "number" then
print("Health must be a number!")
else
rawset(t, key, value)
end
end
}
setmetatable(player, meta)
player.health = "high" -- Вывод: Health must be a number!
player.health = 100 -- РазрешеноИспользуйте rawset(t, key, value), чтобы обойти метатаблицу и напрямую установить значения в таблице. Это предотвращает бесконечные циклы при установке значений внутри __newindex.
Попробуйте сами
function createValidatedTable(key, value)
-- Напишите код здесь
end
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Logic & Flow
1Продвинутая итерация таблиц
Итерация с помощью pairs()Итерация с помощью ipairs()pairs() против ipairs()Повторение — Лист персонажа2Дополнительные функции библиотеки table
table.concat()Создание таблиц и unpack()table.sort()Пользовательская сортировка с помощью функцийИтоги — Таблица рекордов