Lua元表示例:__index和__newindex

元表里面有元方法
设置元表的方法:

setmetatable(tab, metatable

__index元方法

prototype = {x = 0, y = 0, width = 100, height = 100}

local metatable = {}  -- 元表
metatable.__index = function(_, key)	--元表里面有__index元方法
	return prototype[key]
end

-- 构造函数
function new(o)
	setmetatable(o, metatable)
	return o
end

w = new{x=10, y=20}
print(w.width)

w没有width这个字段,但是有一个带有__index方法的元表,于是Lua会将wwidth一起传入__index方法

当元方法是函数,Lua会以表和不存在的键作为参数调用该函数。

当元方法是一个表,Lua会访问这个表。也就是说,将4到6行替换成:

metatable.__index = prototype

可以得到相同的结果。

当元方法是一个函数,开销会比表更昂贵,但是更灵活,例如可以通过函数实现多继承、缓存等特性。


__newindex元方法

__index用于表的查询
__newindex用于表的更新

对表中不存在的键赋值,Lua会查找__newindex元方法,如果该原方法是一个函数,则调用它,不赋值。如果该方法是一个表,则在此表赋值,而不是在原始的表中赋值。

组合使用这两种元方法,可以实现只读的表、具有默认值的表、继承。


应用1:具有默认值的表

function SetDefaultValue(t, d)
	local metatable = {__index = function() return d end}
	setmetatable(t, metatable)
end

tab = {x=10, y=20}
print(tab.x, tab.z) -- 10 nil

SetDefaultValue(tab, 0)
print(tab.x, tab.z) -- 10 0

调用了SetDefaultValue之后,访问tab中不存在的z字段,会先调用其__index元方法,该方法返回0,所以有默认值。

上述写法开销较大,因为这会为每一个表都创建一个新的闭包和一个新的元表。

以下写法能够使得所有的表都使用一个元表:

local metatable = {__index = function (t) return t.xxx end}
function SetDefaultValue(t, d)
	t.xxx = d
	setmetatable(t, metatable)
end

tab = {x=10, y=20}
print(tab.x, tab.z) -- 10 nil

SetDefaultValue(tab, 0)
print(tab.x, tab.z) -- 10 0

如果担心命名冲突,可以使用唯一的键:

local key = {}

local metatable = {__index = function (t) return t[key] end}
function SetDefaultValue(t, d)
	t[key] = d
	setmetatable(t, metatable)
end

tab = {x=10, y=20}
print(tab.x, tab.z) -- 10 nil

SetDefaultValue(tab, 0)
print(tab.x, tab.z) -- 10 0

参考资料:《Lua程序设计 第四版 Programming in Lua, Fourth Edition》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章