table中,可以重新定义的元方法有以下几个:
__add(a,b) 加法
__sub(a,b) 减法
__mul(a,b) 乘法
__div(a,b) 除法
__mod(a,b) 取模
__pow(a,b) 乘幂
__unm(a) 相反数
__concat(a,b) 连接
__len(a) 长度
__eq(a,b) 相等
__lt(a,b) 小于
__le(a,b) 小于等于
__index(a,b) 索引查询
__newindex(a,b,c) 索引更新
__call(a,...) 执行方法调用
__tostring(a) 字符串输出
__metatable 保护元表
Lua处理元表规则:
1.对于二元操作符,如果第一个操作数有元表,并且元表中有所需要的字段定义,那么Lua就以这个字段为元方法,而与第二个值无关。
2.对于二元操作符,如果第一个操作数有元表,但元表中没有需要的字段定义,那么Lua就去查找第二个操作数的元表。
3.如果两个操作数都没有元表,或者都没有对应的元方法定义,Lua就会引发一个错误
__tostring元方法
print(a) (a是一个table)
函数print总是调用tostring来进行格式化输出,当格式化任意值时,tostring会检查该值是否有一个__tostring的元方法
__metatable 保护集合的元表
Set = {}
local mt = {} -- 集合的元表
function Set.new(l)
local set = {}
setmetatable(set, mt)
for _, v in pairs(l) do set[v] = true end
mt.__metatable = "You cannot get the metatable" -- 设置完我的元表以后,不让其他人再设置
return set
end
local tb = Set.new({1, 2})
print(tb)
print(getmetatable(tb))
setmetatable(tb, {})
当设置后,getmetatable就会返回这个字段的值,而setmetatable则会引发一个错误
上述代码打印如下:
{1, 2}
You cannot get the metatable
lua: test.lua:56: cannot change a protected metatable
__index元方法
1.当访问一个table字段时,如果table有这个字段,则直接返回对应值,。
2.当table没有这个字段,则会促使解释器去查找一个__index的元方法,接下来就会调用对应的元方法,返回元方法返回值。
3.如果没有这个元方法,那么就返回nil结果
Windows = {} -- 创建一个命名空间
-- 创建默认值表
Windows.default = {x = 0, y = 0, width = 100, height = 100, color = {r = 255, g = 255, b = 255}}
Windows.mt = {} -- 创建元表
-- 声明构造函数
function Windows.new(o)
setmetatable(o, Windows.mt)
return o
end
-- 定义__index元方法
Windows.mt.__index = function (table, key)
return Windows.default[key]
end
local win = Windows.new({x = 10, y = 10})
print(win.x) -- >10 访问自身已经拥有的值
print(win.width) -- >100 访问default表中的值
print(win.color.r) -- >255 访问default表中的值
在实际编程中,__index元方法不必一定是一个函数,它还可以是一个table。当它是一个函数时,Lua以table和key作为参数来调用该函数,这就和上面的代码一样;当它是一个table时,Lua就以相同的方式来重新访问这个table,所以上面的代码也可以这样表示:
-- 定义__index元方法
Windows.mt.__index = Windows.default
具体应用(项目里面的物品表)
Config = Config or {}
Config.Goods = {
[1] = {1, [[普通装备]]}
}
local Goods_Index = {
goods_id = 1,
goods_name = 2,
}
local mt_Goods = {
__index = function(t,k)
return rawget(t, Goods_Index[k])
end,
}
for k1, v1 in pairs(Config.Goods) do
setmetatable(v1, mt_Goods)
end
print(Config.Goods[1].goods_name) -->普通装备
rawget(tb,i)对table tb进行一次“原始(raw)”访问,不会访问其元表。
__newindex元方法
__newindex用于更新table中的数据,而__index用于查询table中的数据,当对一个table中不存在的索引值赋值时,
1.Lua解释器先判断这个table是否有元表
2.如果有了元表,就查找元表中是否有__newindex元方法;如果没有元表,就直接添加这个索引,然后对应的赋值。
3.如果有这个__newindex元方法,Lua解释器就会执行它,而不是执行赋值。
4.如果这个__newindex对应的不是一个函数,而是一个table时,Lua解释器就在table中执行赋值,而不是对原来的table。
rawset(t,k,v),可以不涉及任何元方法而直接设置table t与key k相关联的value v。