__newindex 元方法
本课属于 Coddy Lua 学习旅程的“逻辑与流程”单元。第 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 函数,并传入 3 个参数:表本身、要设置的键,以及要赋的值。
在这个示例中,函数只会打印一条消息,并不会实际存储任何内容。
你也可以使用 __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 存储
player.health = "high" -- 没有提示:health 现在已存在注意第二个示例中使用了 rawset()。这个函数会绕过元表,直接在表中设置值。
没有它,设置值会再次触发 __newindex,从而造成无限循环。
因为 __newindex 只会在表中不存在某个键时运行,所以这个检查会验证每个键的首次赋值。rawset 存储了 health 后,后续赋值会直接修改它。若要检查每次赋值,可以让表本身保持为空,并将值存储在第二个表中,再通过 __index 读取这些值:
local data = {}
local player = setmetatable({}, {
__index = data,
__newindex = function(t, key, value)
if key == "health" and type(value) ~= "number" then
print("Health must be a number!")
else
data[key] = value
end
end
})
player.health = 100
player.health = "high" -- 输出:Health must be a number!
print(player.health) -- 输出:100player 本身从未获得任何键,因此每次赋值都会经过 __newindex,每次读取都会通过 __index 访问 data。
挑战
简单编写一个函数 createValidatedTable,它接收 key 和 value,并返回一个只接受新键对应的字符串值的表。
创建一个空表,并为它附加一个元表,其中的 __newindex 会验证值的类型。如果有人尝试添加非字符串值,就使用 rawset() 存储字符串 "invalid"。如果值是字符串,则使用 rawset() 正常存储。
逻辑:
- 创建一个空表
- 创建一个元表,其中的
__newindex接收表、键和值 - 在
__newindex中,使用type(value) == "string"检查值是否为字符串 - 如果值是字符串,使用
rawset(t, key, value)存储它 - 如果值不是字符串,使用
rawset(t, key, "invalid")存储"invalid"作为替代值 - 使用
setmetatable()将元表附加到表上 - 使用
rawset()将初始键值对添加到表中(绕过元表) - 返回该表
参数:
key(字符串):要添加到表中的初始键value(字符串):要添加到表中的初始值
返回值:一个经过验证、只接受字符串值且包含初始键值对的表(table)
亲手试试
function createValidatedTable(key, value)
-- 在这里编写代码
end
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
逻辑与流程中的所有课
自己练习: Lua 在线编译器