对于一个table而言,如果右值引用一个不存在的变量,会引发nil错误,但如果左值引用一个不存在的变量则仅会创建一个变量。这是一种非常危险的行为,会导致拼写错误无法被检测出来。
解决的办法是:当确认table不应该动态增加变量时(table动态增加变量应该受到应有的限制)就是用元表(metatable)对左值访问进行限制:
t = {}
setmetatable(t,{ __newindex = function( t,k,v )
print(debug.traceback())
assert(false, "Invalid key "..k.." has been left-refrenced.")
end })
上述为实现原理,如果table不是由工厂创建的,那么将会很难控制其访问。对于一个工厂创建的表,则可以在最后返回创建元素前将__newindex锁定从而实现控制访问。
NewIndexError = function( k )
print( debug.traceback() )
assert( false, "Invalid key "..k.." has been left-refrenced." )
end
AppleFactory = {}
AppleFactory.OnEat = function( self, ... ) end
AppleFactory.New = function()
local apple = {}
apple.Color = "red"
apple.OnEat = AppleFactory.OnEat
--......
getmetatable(apple).__newindex = function(t,k,v)
-- 先处理继承等等
if ...
else
-- 如果需要的特殊处理都没有进行,可以认为是一个拼写错误或非法添加变量
NewIndexError( k )
end
end
end
在这里引出一个比较核心的思想:由工厂管理表。