什么是Metatable Lua中Metatable这个概念, 国内将他翻译为元表. 元表为重定义Lua中任意一个对象(值)的默认行为提供了一种公开入口. 如同许多OO语言的操作符重载或方法重载. Metatable能够为我们带来非常灵活的编程方式. 具体的说, Lua中每种类型的值都有都有他的默认操作方式, 如, 数字可以做加减乘除等操作, 字符串可以做连接操作, 函数可以做调用操作, 表可以做表项的取值赋值操作. 他们都遵循这些操作的默认逻辑执行, 而这些操作可以通过Metatable来改变. 如, 你可以定义2个表如何相加等. 看一个最简单的例子, 重定义了2个表的加法操作. 这个例子中将c的__add域改写后将a的Metatable设置为c, 当执行到加法的操作时, Lua首先会检查a是否有Metatable并且Metatable中是否存在__add域, 如果有则调用, 否则将检查b的条件(和a相同), 如果都没有则调用默认加法运算, 而table没有定义默认加法运算, 则会报错. --定义2个表
a = {5, 6} b = {7, 8} --用c来做Metatable c = {} --重定义加法操作 c.__add = function(op1, op2) for _, item in ipairs(op2) do table.insert(op1, item) end return op1 end --将a的Metatable设置为c setmetatable(a, c) --d现在的样子是{5,6,7,8} d = a + b 有了个感性的认识后, 我们看看Metatable的具体特性.
local Bird = {CanFly = true}
function Bird:New() local b = {} setmetatable(b, self) self.__index = self return b end local Ostrich = Bird:New() --Bird.CanFly is true, Ostrich.CanFly is true Ostrich.CanFly = false --Bird.CanFly is true, Ostrich.CanFly is false __newindex与__index相对应, 在对table的key做更新时触发. 可以使用rawset和rawget对table的key操作来跳过这些事件的触发. 调用与截获 Java与C#中需要费不少周折来实现动态代理和AOP, 类似这样的功能在Lua中确很简单, 虽然被限制了很多, 但你依然能够感受到Lua的灵活. 这就是__call操作, 当值被调用时触发. 这里我将table类型的a做了一个函数方式的调用a(), 会触发__call. 另一个应用示例可以参见我的另一篇文章Lua中实现类似C#的事件机制 a = {}
function a:Func() print("simonw") end c = {} c.__call = function(t, ) print("Start") t.Func() print("End") end setmetatable(a, c) a() --[[ Start simonw End ]] 这里的示例都是以最简单的方式展现, 以便能更清晰的描述核心, 更多的资料以及具体应用请参考Programming In Lua和Lua参考手册. |
|