转载请注明出处:csdn火木达人
一、基本介绍:
1.Lua中的每个值都可以有一个metatable,这个 metatable 就是一个原始的 Lua table (metatable 中的键名为 事件 (event) ,把其中的值叫作 元方法 (metamethod))
2.getmetatable函数来查询到任何一个值的 metatable
3.setmetatable 函数来替换掉 table 的 metatable
4.在Lua代码中只能设置table的元表,其它类型值的元表只能在C代码中设置
5.每个 table 和 userdata 拥有独立的 metatable (当然多个 table 和 userdata 可以共享一个相同的表作它们的 metatable); 其它所有类型的值,每种类型都分别共享唯一的一个 metatable
6.Lua在创建新table时默认不会创建元表
7.一个table也可以作为自己的元表
8.一个 metatable 可以控制一个对象做数学运算操作、比较操作、连接操作、取长度操作、取下标操作时的行为, metatable 中还可以定义一个函数,让 userdata 作垃圾收集时调用它
二、metatable 可以控制的操作
1.算术类元方法
__add(加法"+") __sub(减法"-") __mul(乘法"*") __div(除法"/") __unm(相反数"-") __mod(取模"%") __pow(乘幂"^") __concat(连接"..") __len(求长度"#")
先找第一个值的元方法,如果没有再找第二个值的元方法,都没有报错
add:+操作实例:
local tableA = {1,3,"abc"};
local tableB = {5,7};
local mt = {};
mt.__add = function(t1,t2)
for _,item in ipairs(t2) do
table.insert(t1,item);
end
return t1;
end
setmetatable(tableA,mt);
setmetatable(tableB,mt);
local tableSum = tableA + tableB;
for k,v in ipairs(tableSum) do
print("k = " .. k .. " -- v = " .. v);
end
2.关系类元方法
__eq(等于) __lt(小于) __le(小于等于)
没有大于和不等于元方法,但可以转化实现
a~=b转化为not(a==b)
a>b转化为b<a
a>=b转化为b<=a
eq: == 操作实例。 仅仅在参于比较的两个对象类型相同且有对应操作相同的元方法时才起效。
local tableA = {1,3,"abc"};
local tableB = {5,7};
mt.__eq = function(t1,t2)
print("eq");
return true;
end
setmetatable(tableA,mt);
setmetatable(tableB,mt);
print("eq = " .. tostring(tableA == tableB));
3.库定义元方法
__tostring(字符串转换)
tostring函数会用此元方法进行转换
__metatable(指向元方法)
setmetatable、getmetatable会访问这个元方法
如果设置成其它内容就可以起到保护元表的功能
__mode(弱引用table模式)
它的值是一个字符串
如果包含"k"则表示table里的key是弱引用模式
如果包含"v"则表示table里的value是弱引用模式
tostring: 字符串化实例
local tableA = {1,3,"abc"};
mt.__tostring = function(t)
return "__tostring" -- 必须返回一个字符串
end
setmetatable(tableA,mt);
print(tableA)
4.table访问的元方法
__index(访问表中不存在的字段)
(1)当没有这个元方法时访问不存在字段会返回nil
(2)当有元方法时两种访问形式
作为函数时有两个参数,第一个是被访问的table,第二个是不存在的key
作为table时就从这个table里找被访问的table里不存在的这个key
(3)通常用于实现继承特性
(4)作为函数的时候开销会大一些,但更灵活,可以实现多重继承和缓存等功能
(5)如果不想涉及元方法,可以使用rawget(t,i)"原始访问",不会加速代码执行
__newindex(给表中不存在的字段赋值)
(1)当没有这个元方法时会在被访问的table里创建新字段并赋值
(2)当有元方法时两种访问形式
作为函数时有三个参数,第一个是被访问的table,第二个是不存在的key,第三个是value
作为table时,会在这个table里赋值而不是在被访问table里赋值
(3)可以使用rawset(t,k,v)绕过元方法赋值
可以利用这两个元方法实现很多table的特殊功能
(1)具有默认值的table,把带有值的table作为__index元方法
(2)跟踪table的访问
t = {} --原table
local _t = t --私有化访问
t = {} --创建代码,名字相同
mt = {}
mt.__index = function(t,k)
print("access "..tostring(k))
return _t[k] --访问原来的table
end
mt.__newindex = function(t,k,v)
print("update "..tostring(k).." to "..tostring(v))
_t[k] = v --更新原来的table
end
setmetatable(t, mt)
但这个例无法遍历原来的table,pairs只能操作代理table
(3)只读table,__index指向被访问table,__newindex弹错