http://www.cnblogs.com/simonw/archive/2007/01/17/622032.html

什么是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的具体特性.

Metatable并不神秘, 他只是一个普通的table, 在table这个数据结构当中, Lua定义了许多重定义这些操作的入口. 他们均以双下划线开头为table的域, 如上面例子的__add. 当你为一个值设置了Metatable, 并在Metatable中设置了重写了相应的操作域, 在这个值执行这个操作的时候就会触发重写的自定义操作. 当然每个操作都有每个操作的方法格式签名, 如__add会将加号两边的两个操作数做为参数传入并且要求一个返回值. 有人把这样的行为比作事件, 当xx行为触发会激活事件自定义操作.

Metatable中定义的操作add, sub, mul, div, mod, pow, unm, concat, len, eq, lt, le, tostring, gc, index, newindex, call...

在Lua中任何一个值都有Metatable, 不同的值可以有不同的Metatable也可以共享同样的Metatable, 但在Lua本身提供的功能中, 不允许你改变除了table类型值外的任何其他类型值的Metatable, 除非使用C扩展或其他库. setmetatable和getmetatable是唯一一组操作table类型的Metatable的方法.

Metatable与面向对象

Lua是个面向过程的语言, 但通过Metatable可以模拟出面向对象的样子. 其关键就在于__index这个域. 他提供了表的索引值入口. 这很像重写C#中的索引器, 当表要索引一个值时如table[key], Lua会首先在table本身中查找key的值, 如果没有并且这个table存在一个带有__index属性的Metatable, 则Lua会按照__index所定义的函数逻辑查找. 仔细想想, 这不正为面向对象中的核心思想继承, 提供了实现方式么. Lua中实现面向对象的方式非常多, 但无论哪种都离不开__index.

这个例子中我使用了Programming In Lua中的实现OO的方式, 建立了Bird(鸟)对象, 拥有会飞的属性, 其他鸟对象基于此原型, Ostrich(鸵鸟)是鸟的一种但不会飞. 结果很明显, Bird和Ostrich分别有独立的状态.

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参考手册.

Metatable In Lua 浅尝辄止的更多相关文章

  1. Lua的元表(metatable)

    metatable允许我们改变table的行为 > Lua中的每一个表都可以有metatable(后面我们将看到userdata也有Metatable) > Lua默认创建一个不带meta ...

  2. Lua之元表

    Lua之metatable 一.元表 Lua中的每个值都有一套预定义的操作集合,也可以通过metatable(元表)来定义一个值的行为,metatable包含了一组meatmethod(元方法). L ...

  3. Metatable和Metamethod

    Metatable和Metamethod是用来干啥的?它们可以使得表a和b的表达式“a + b”变得有意义,其中metatable使两个不相关的表a和b之间可以进行操作,而操作的具体行为比如说&quo ...

  4. [转]LUA 学习笔记

    Lua 学习笔记 入门级 一.环境配置 方式一: 1.资源下载http://www.lua.org/download.html 2.用src中的源码创建了一个工程,注释调luac.c中main函数,生 ...

  5. 转:Lua简明教程

    需要注意的是:lua中的变量如果没有特殊说明,全是全局变量,那怕是语句块或是函数里. 这里很奇怪,为什么在函数内部声明的变量默认也是global的呢? 函数的返回值 和Go语言一样,可以一条语句上赋多 ...

  6. Metatable和Metamethod(转)

    Metatable和Metamethod是用来干啥的?它们可以使得表a和b的表达式“a + b”变得有意义,其中metatable使两个不相关的表a和b之间可以进行操作,而操作的具体行为比如说&quo ...

  7. Lua高级教程Metatables

    什么是Metatable metatable是Lua中的重要概念,每一个table都可以加上metatable,以改变相应的table的行为. Metatables举例 -- 声明一个正常的关系变量 ...

  8. [Lua]Lua高级教程Metatables

    什么是Metatable metatable是Lua中的重要概念,每一个table都可以加上metatable,以改变相应的table的行为. Metatables举例 -- 声明一个正常的关系变量 ...

  9. 学习Lua setmetatable Lua 元表

    Lua 元表(Metatable) 在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作. 个人理解,这个相当于其他语言的继承,是把这个类的方法 ...

随机推荐

  1. caffe2--Install

    Install Welcome to Caffe2! Get started with deep learning today by following the step by step guide ...

  2. ssh 保持连接

    ssh 保持连接 使用 ssh 登陆到云主机上,一段时间没有操作终端,会发现 ssh 连接断了,终端无响应. 配置 ssh 客户端,使其以一定间隔时间向 sshd 服务端发送心跳包,可解决此问题. / ...

  3. CentOS 安装和配置 Mantis

    Mantis是一个基于PHP技术的轻量级的开源缺陷跟踪系统,以Web操作的形式提供项目管理及缺陷跟踪服务.在功能上.实用性上足以满足中小型项目的管理及跟踪.更重要的是其开源,不需要负担任何费用. 1. ...

  4. c#生成试卷。。。

    .net下,操作Word的插件有NPOI,Spire,一版大家经常用的是NPOI,我在着手开发的时候,优先考虑的也是NPOI,然而时间比较着急,没有找到NPOI支持2003版本, 就放弃了,从网上发行 ...

  5. EasyDarwin接入ffmpeg实现264转图片快照功能

    本文转自:http://blog.csdn.net/cai6811376/article/details/51774467 EasyDarwin一直坚持开源精神,所以我们着手把EasyDarwin中使 ...

  6. MySQL 的“root”用户修改密码

    MySQL 的“root”用户默认状态是没有密码的,所以在 PHP 中您可以使用 mysql_connect("localhost","root"," ...

  7. LightOJ1197 Help Hanzo —— 大区间素数筛选

    题目链接:https://vjudge.net/problem/LightOJ-1197 1197 - Help Hanzo    PDF (English) Statistics Forum Tim ...

  8. java连接oracle的几种方式

    jdbc连接(驱动包ojdbc6.jar)String driver = "oracle.jdbc.OracleDriver"; //驱动标识符 String url = &quo ...

  9. JMeter参数化设置——通过函数助手

    Now you can know everything in the world, but the only way you're findin' out that one is by givin' ...

  10. C++中map容器的说明和使用技巧

    C++中map容器提供一个键值对容器,map与multimap差别仅仅在于multiple允许一个键对应多个值. 一.map的说明 1 头文件 #include <map> 2 定义 ma ...