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++中运算符重载的效果。
实例:
1 Set = {} 2 Set.mt = {} 3 --setmetatable(Set.mt, Set.mt) 4 5 function Set:new( t ) 6 local tb = t or {} 7 setmetatable(tb, Set.mt) 8 return tb 9 end 10 11 function Set.union( a, b ) 12 local res = Set.new() 13 for _, k in pairs(a) do 14 table.insert(res, k) 15 end 16 for _, k in pairs(b) do 17 table.insert(res, k) 18 end 19 return res 20 end 21 22 function Set.tostring( set ) 23 local s = "{ " 24 local sep = " " 25 for _, k in pairs(set) do 26 if type(k) == "boolean" then 27 k = "boolean" 28 elseif type(k) == "function" then 29 k = "function" 30 elseif type(k) == "table" then 31 -- k = "table" 32 k = Set.tostring(k) 33 end 34 s = s.." [".._.."]".." = "..k.." " 35 end 36 return s.."}" 37 end 38 39 local s1 = Set:new({10, 20, 30, 50}) 40 local s2 = Set:new({40, 60}) 41 42 Set.mt.__add = Set.union 43 Set.mt.__concat = Set.union 44 Set.mt.__tostring = Set.tostring 45 46 local s3 = s1 + s2 47 print(s3) 48 49 local s4 = s1..s2 50 print(s4) 51 52 这里+和..是等价的,所以s3==s4,结果: 53 { 54 [1] = 10 55 [2] = 20 56 [3] = 30 57 [4] = 40 58 [5] = 50 59 [6] = 60 60 } 61 { 62 [1] = 10 63 [2] = 20 64 [3] = 30 65 [4] = 40 66 [5] = 50 67 [6] = 60 68 }
当我们对两个有不同metatable的表进行加操作时,则检查第一个表的metatabled是否有__add,有则用之,
没有就再去检查第二个表。。。还没有就报错。
__index:
我们在访问一个表不存在的域时,lua解释器会去查找metatabled中是否有__index方法(metamethod),如果不存在则返回nil,
否则由__index方法返回。__index可以是函数也可以是表
1 Set.mt.__index = function (tb, key)--这里会传进来一个table和key 2 return "get "..key.." is null" 3 end 4 5 -- Set.mt.__index = {sex = "man"} 6 7 local myTb = Set:new({name = "Mical", age = 12}) 8 print(myTb.name) 9 print(myTb.age) 10 print(myTb.sex) 11 12 结果: 13 Mical 14 12 15 get sex is null
__newindex:
当你给表不存在的值赋值时,lua解释器就会查找对应Metatable中的__newindex方法(metamethod)。如果存在则调用这个函数而不进行赋值。
1 Set.mt.__newindex = function (tb, key, val) 2 print("can't set "..key.." value") 3 end 4 local myTb = Set:new({name = "Mical", age = 12}) 5 myTb.sex = "man"
结果:
can't set sex value
rawset(t, k, v):
不调用任何的metamethod 对表t的k域赋值为v,上述例子就可以用rawset(myTb, "sex", "man")赋值。
给表设置默认值:
1 local key = {} 2 local mt = {__index = function(t) return t[key] end} 3 function setDefault(t, d) 4 t[key] = d 5 setmetatable(t, mt) 6 end
监控表:
index用于查询,__newindex用于更新,它们都是在访问的域不存在的时候才起作用。如果我们想捕获对一个表的所有反问,可以通过保持这个表为空来实现。
1 local index = {} 2 local mt = { __index = function(t, k) 3 print("access to element "..tostring(k)) 4 return t[index][k] 5 end, 6 __newindex = function(t, k, v) 7 print("update of element "..tostring(k).." to "..tostring(v)) 8 t[index][k] = v 9 end 10 } 11 local function track(t) 12 local proxy = {} 13 proxy[index] = t 14 setmetatable(proxy, mt) 15 return proxy 16 end 17 18 local myTb = {} 19 local nowTb = track(myTb) 20 nowTb.name = "mical" 21 print(nowTb.name) 22 23 nowTb.age = 12 24 print(nowTb.sex) 25 26 for k, v in pairs(nowTb) do 27 print(k, " ", v); 28 end