元表
- 基础知识
lua中的每个值都有一个元表。table和userdata都可以有各自独立的元表,而其它类型的值则共享其类型所属的单一元表。Lua在创建新的table时不会创建元表。任何table都可以作为任何值的元表,而一组相关的table也可以共享一个通用的元表,此元表描述了他们共同的行为。一个table甚至可以作为他自己的元表,用于描述其特有的行为。总之任何搭配形式都是合法的。
- 普通元表.
- 弱引用table(Weak Table)
- 备忘录元表(这项技术也需要用到弱引用table),它能使具有相同默认值的table复用同一个元表。
local t = {}
print(getmetatable(t)) -->nil
local t1 = {}
setmetatable(t, t1)
print(getmetatable(t) == t1) -->true
在lua代码中,只能设置table的元表。若要设置其它类型的值的元表,则必须通过C代码来完成。标准的字符串程序库为所有的字符串都设置了一个元表,而其他类型在默认情况中都没有元表。
print(getmetatable("hi")) -->table: 000000001ED78460
print(getmetatable("hi") == getmetatable("h")) -->true
print(getmetatable(10)) -->nil
- setmetatable(t, mt) 设置一个table t的元表mt
- getmetatable(t) 获取table t的元表
元方法
- 算数类的元方法
- 关系类的元方法
- 库定义的元方法
- table访问的元方法(类似于get和set)
跟踪table的访问(使用代理)
这段主要讲述为了捕捉到所有对table的访问所做的常规应用方法。解释如下:
__index和__newindex都是在table中没有所需访问的index时才发挥作用的。因此,只有将一个table保持为空,才有可能捕捉到所有对它的访问。为了监视一个table的所有访问,就应该为真正的table创建一个代理。这个代理就是一个空的table。其中__index和__newindex元方法可以用于跟踪所有的访问,并将访问重定向到原来的table上。那么可以这么做:
--[[
此处代码按照书上讲解copy,不过为了避免看书自己看错,把变量名作了区分,避免新人无法理解看错的问题。
--]]
local t = {} --原来的table(在其它地方创建的)
--保持对原table的一个私有访问
local _t = t
--创建代理
local tproxy = {}
--创建元表
local mt = {
__index = function(t, k)
print("access to element "..tostring(k))
return _t[k] --访问原来的table
end,
__newindex = function(t, k, v)
print("*update of element "..tostring(k).."to "..tostring(v))
_t[k] = v --更新原来的table
end
}
setmetatable(tproxy, mt)
tproxy[2] = "hello"
--> *update of element 2to hello
print(tproxy[2])
-->access to element 2
-->hello
只读的table(通过代理的概念实现)
function readOnly(t)
local proxy = {}
local mt = { --创建元表
__index = t,
__newindex = function(t, k, v)
error("attempt to update a read-only table", 2)
end
}
setmetatable(proxy, mt)
return proxy
end
local days = readOnly{"Sunday", "Monday", "tuesday", "Wednesday", "Thursday",
"Friday", "Saturday"}
print(days[1]) -->Sunday
days[2] = "Noday" --直接报错