lua的Metatables和Metamethods

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
原文地址:https://www.cnblogs.com/wrbxdj/p/5365272.html