您的位置:首页 > 编程语言 > Lua

lua的Metatables和Metamethods

2016-04-07 20:20 585 查看
Metatable:

  lua中的每一个表都有其Metatable,默认情况下Metatable为nil。可通过setmetatable函数设置或者改变一个表的Metatable,

也可以通过getmetatable得到一个表的Metatable。任何一个表都可以是其它表的Metatable,可以多个表共享一个Metatable,

一个表也可以是自身的Metatable。

作用:使我们可以改变table的行为

  在metatable中设置__add, __sub, __mul, __div, __eq(等于), __lt(小于), __le(小于等于), __unm(负), __pow(幂),

__concat (定义连接行为),__tostring等metamethod方法,可以实现类似于C++中运算符重载的效果。

实例:

Set = {}
Set.mt = {}
--setmetatable(Set.mt, Set.mt)

function Set:new( t )
local tb = t or {}
setmetatable(tb, Set.mt)
return tb
end

function Set.union( a, b )
local res = Set.new()
for _, k in pairs(a) do
table.insert(res, k)
end
for _, k in pairs(b) do
table.insert(res, k)
end
return res
end

function Set.tostring( set )
local s = "{\n"
local sep = "  "
for _, k in pairs(set) do
if type(k) == "boolean" then
k = "boolean"
elseif type(k) == "function" then
k = "function"
elseif type(k) == "table" then
-- k = "table"
k = Set.tostring(k)
end
s = s.."   [".._.."]".." = "..k.."\n"
end
return s.."}"
end

local s1 = Set:new({10, 20, 30, 50})
local s2 = Set:new({40, 60})

Set.mt.__add = Set.union
Set.mt.__concat = Set.union
Set.mt.__tostring = Set.tostring

local s3 = s1 + s2
print(s3)

local s4 = s1..s2
print(s4)

这里+和..是等价的,所以s3==s4,结果:
 {
[1] = 10
[2] = 20
[3] = 30
[4] = 40
[5] = 50
[6] = 60
}
{
[1] = 10
[2] = 20
[3] = 30
[4] = 40
[5] = 50
[6] = 60
}


  当我们对两个有不同metatable的表进行加操作时,则检查第一个表的metatabled是否有__add,有则用之,
没有就再去检查第二个表。。。还没有就报错。

__index:

  我们在访问一个表不存在的域时,lua解释器会去查找metatabled中是否有__index方法(metamethod),如果不存在则返回nil,

否则由__index方法返回。__index可以是函数也可以是表

Set.mt.__index = function (tb, key)--这里会传进来一个table和key
return "get "..key.." is null"
end

-- Set.mt.__index = {sex = "man"}

local myTb = Set:new({name = "Mical", age = 12})
print(myTb.name)
print(myTb.age)
print(myTb.sex)

结果:
Mical
12
get sex is null


__newindex:

  当你给表不存在的值赋值时,lua解释器就会查找对应Metatable中的__newindex方法(metamethod)。如果存在则调用这个函数而不进行赋值。

Set.mt.__newindex = function (tb, key, val)
print("can't set "..key.." value")
end
local myTb = Set:new({name = "Mical", age = 12})
myTb.sex = "man"

结果:
can't set sex value


rawset(t, k, v):

  不调用任何的metamethod 对表t的k域赋值为v,上述例子就可以用rawset(myTb, "sex", "man")赋值。

给表设置默认值:

local key = {}
local mt = {__index = function(t) return t[key] end}
function setDefault(t, d)
t[key] = d
setmetatable(t, mt)
end


监控表:

index用于查询,__newindex用于更新,它们都是在访问的域不存在的时候才起作用。如果我们想捕获对一个表的所有反问,可以通过保持这个表为空来实现。

local index = {}
local mt = { __index = function(t, k)
print("access to element "..tostring(k))
return t[index][k]
end,
__newindex = function(t, k, v)
print("update of element "..tostring(k).." to "..tostring(v))
t[index][k] = v
end
}
local function track(t)
local proxy = {}
proxy[index] = t
setmetatable(proxy, mt)
return proxy
end

local myTb = {}
local nowTb = track(myTb)
nowTb.name = "mical"
print(nowTb.name)

nowTb.age = 12
print(nowTb.sex)

for k, v in pairs(nowTb) do
print(k, "\n", v);
end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: