metatable和metamethod - abbshr/Lua-newbie GitHub Wiki
Metatables允许我们改变 table 的行为
每个表都可以有其Metatable.
任何一个表都可以是其他一个表的 metatable,
一组相关的表可以共享一个 metatable(描述他们共同的行为).
一个表也可以是自身的 metatable(描述其私有行为)
创建一个不带metatable的表:
t = {}
print(getmetatable(t)) -- nil
使用setmetatable
函数设置/改变一个表的metatable:
mt = {}
setmetatable(t, mt)
print(getmetatable(t) == mt) -- true
定义table的算术运算
对于每个算数运算法,metatable都有对应的域:
__add加,__sub减,__mul乘,__div除,__unm负,__pow幂
把相应的域赋值自定义函数,即可在调用算数运算时生效:
mt.__add = func
当对两个不同metatable的操作数执行算数运算时,选择metatable的原则是:
如果第一个操作数的metatable有相应的域,则使用这个metatable,否则查看第二个操作数,否则报错
定义table的关系运算
__eq等于,__lt小于,__le小于等于
关系运算的metatable不支持混合类型运算.
如果两个操作数的metatable不同,则直接报错.但相等操作除外:如果类型不等直接返回false
其他metamethod
print函数通常会调用tostring来格式化输出,而tostring又会调用参数的metatable的__tostring域.
setmetatable/getmetatable会使用__metatable域: 可用于metatable的保护
设置table的__metatable后,setmetatable则会报错,而getmetatable会返回__metatable的值
与表相关的metamethod
__index
,当在一个table中找不到指定索引时,将查询__index.
mt.__index = function(table, index) end
或
mt.__index = {...}
如果不想访问__index域,可以使用raw access方式: rawget(t, i)
函数
__newindex
,当对表的缺省域赋值,如果定义了__newindex域则调用这个函数而不是对表进行insert赋值,如果__newindex是一个table,则对这个table进行insert赋值
如果希望对原始表进行操作,可以调用rawset(t, k, v)
函数
实现一个监控表
---./trackt.lua
local prindex = {};
local mt = {
__index = function (t, i)
print('access ' .. tostring(i))
return t[prindex][i]
end,
__newindex = function (t, i, v)
print('update ' .. tostring(i))
t[prindex][i] = v
end
}
function trackt(t)
-- body
local proxy = {}
proxy[prindex] = t
setmetatable(proxy, mt);
return proxy;
end
--- 最后导出trackt函数
return trackt