Lua中的__index和__newindex实例

前言

这篇博文将通过几个简单的实例演示,巩固对__index和__newindex的理解,同时加深对Lua中元表和元方法的理解,如果对Lua的元表和元方法还不是很熟悉的话,请参考这篇文章:《Lua中的元表与元方法》。

具有默认值的table

我们都知道,table中的任何字段的默认值都是nil,但是通过元表,我们可以很容易的修改这一规定,代码如下:

代码如下:

function setDefault(tb, defaultValue)
     local mt = {__index = function () return defaultValue end}
     setmetatable(tb, mt)
end
 
local tb1 = {x = 10, y = 20}
print(tb1.x, tb1.z)     --> 10 nil
setDefault(tb1, 100) -->设置默认值
print(tb1.x, tb1.z) --> 10 100 这里打印的就是默认值

可以看到,在代码中,setDefault函数为所有需要默认值的table创建了一个新的元表。如果准备创建很多需要默认值得table,这种方法的开销或许就比较大了。由于在元表中默认值defaultValue是与元方法关联在一起的,所以setDefault无法为所有table都使用同一个元表。如果要让具有不同默认值得table都使用同一个元表,那么就需要将每个元表的默认值存放在table本身中,可以使用一个额外的字段来存储默认值。例如以下代码:

代码如下:

local mt = {__index = function (t) return t.___ end}
function setDefault(tb, defaultValue)
     tb.___ = defaultValue       -- 非常谢谢hellowei犀利的review。具体请参见评论
     setmetatable(tb, mt)
end

上面代码中的“___”是为了防止名字冲突而起的名字;如果这样的话,你还担心名字冲突,确保key在table中的唯一性,只需要创建一个新的table,并用它作为key即可,每一个新创建的table都是一个唯一的地址,比如以下代码:

代码如下:

local key = {} -- 唯一的key
local mt = {__index = function (tb) return tb[key] end}
 
function setDefault(tb, defaultValue)
     tb[key] = defaultValue
     setmetatable(tb, mt)
end

记录table的访问

有的时候,一种特定的需求,我们需要记录对一个table的所有访问,不管是查询还是更新,我们都需要记录日志。这如何完成?我们都知道,元表中的__index和__newindex是在table中没有所需要访问的index时才发挥作用的,因此,只有将一个table保持为空,然后设置__index和__newindex元方法,才有可能记录下来所有对它的访问。

为了监视一个table的所有访问,就应该为真正的table创建一个代理。这个代理就是一个空的table,其中__index和__newindex元方法可用于跟踪所有的访问,并将访问重定义到原来的table上。这就是思路,接下来看代码:

代码如下:

local t = {} --原来的table
 
-- 保持对原table的一个引用
local _t = t
 
-- 创建代理
t = {}
 
-- 创建元表
local mt = {
__index = function (t, k)
print("access to element " .. tostring(k))
return _t[k]
end,
 
__newindex = function (t, k, v)
print("update of element " .. tostring(k))
_t[k] = v
end
}
 
setmetatable(t, mt)
 
t.x = 10 -- update of element x
print(t.x) -- access to element x

如果想要同时监视几个table,无须为每个table创建不同的元表;相反,只要以某种形式将每个代理与其原table关联起来,并且所有代理都共享一个公共的元表。这个问题与设置table默认值相关联的问题类似,也是将原来的table保存在代理table的一个特殊的字段中。代码如下:

代码如下:

-- 创建唯一索引
local index = {}
 
-- 创建元表
local mt = {
     __index = function (t, k)
          print("access to element " .. tostring(k))
          return t[index][k]
     end,
 
     __newindex = function (t, k, v)
          print("update of element " .. tostring(k))
          t[index][k] = v
     end
}
 
function track(t)
     local proxy = {}
     proxy[index] = t
     setmetatable(proxy, mt)
     return proxy
end
 
local t = {}
local proxy = track(t)
proxy.x = 10
print(proxy.x)

只读的table

通过代理的概念,可以很容易的实现只读的table。只需要跟踪所有对table的更新操作,并引发一个错误就好了,对于查询时,我们不用去馆,只需要管对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 tbDemo = readOnly{1, 2, 3, 4, 5}
print(tbDemo[1])
tbDemo[1] = 20

元表中__index对应的是原来的table,而更新原来的table时,就会显示错误提示:Attempt to update a read-only table。

总结

这篇文章对Lua中的__index和__newindex的使用进行了详细的讲解和分析,并提供了实际的代码,主要是为了加深对Lua中元表和元方法的理解,元表和元方法在Lua中的地位太总要了,很多高级的编程技巧和特殊需求都是基于元表和元方法来实现了,所以,也希望大家能好好的阅读这篇文章,同时也希望我的文章对大家有帮助。

(0)

相关推荐

  • Lua中的元方法__newindex详解

    好吧,我写文章的进度已经赶不上看书的进度了,简单的几段文字就够我唠叨一篇文章了. 今天继续来说说元方法,与__index有点相似的__newindex元方法. 1.查询与更新 上一篇文章我们介绍了__index元方法,总结来说,__index元方法是用于处理调用table中不存在的字段. 注意,[调用]这个词,只是调用,而不是赋值.   如果,我们要对table中某个不存在的字段赋值呢?(小若:就,直接赋值啊!) 没错,我们直接就能赋值了,不会报错的. 问题是,如果我想监控这个操作呢?如果有人想

  • Lua中__index和__newindex之间的沉默与合作

    因为不想在一篇文章里挤太多知识点,所以,有些小知识点就集合到这样的文章里吧~ 1.沉默技能--拒绝__index和__newindex效果 虽然__index和__newindex是很好用的功能,但是,有时候我们又希望很纯粹地去调用table或者给table赋值. 那,这时候怎么办?给table重新设置一个元表?不,这个做法很糟糕~ 于是,体贴的Lua又给我们提供了这样的调用方式,如下代码: 复制代码 代码如下: local smartMan = {         name = "none&q

  • Lua中的__index和__newindex实例

    前言 这篇博文将通过几个简单的实例演示,巩固对__index和__newindex的理解,同时加深对Lua中元表和元方法的理解,如果对Lua的元表和元方法还不是很熟悉的话,请参考这篇文章:<Lua中的元表与元方法>. 具有默认值的table 我们都知道,table中的任何字段的默认值都是nil,但是通过元表,我们可以很容易的修改这一规定,代码如下: 复制代码 代码如下: function setDefault(tb, defaultValue)      local mt = {__index

  • 理解Lua中的__index和__newindex

    复制代码 代码如下: --example:  local temp_table ={      10,      1,      Index1 = "hello",      Index2 = "world",      Index3 = "lua",      Index4 = "language",      lang = "lua language",  }    temp_table.__add =

  • Lua中调用C语言函数实例

    在上一篇文章(C调用lua函数)中,讲述了如何用c语言调用lua函数,通常,A语言能调用B语言,反过来也是成立的.正如Java与c语言之间使用JNI来互调,Lua与C也可以互调. 当lua调用c函数时,使用了和c调用lua中的同一种栈,c函数从栈中得到函数,然后将结果压入栈中.为了区分返回结果和栈中的其他值,每一个函数返回结果的个数. 这里有个重要的概念:这个栈不是全局的结构,每个函数都有自己的私有局部栈.哪怕c函数调用了lua代码,lua代码再次调用该c函数,他们有各自独立的局部栈.第一个参数

  • Lua中使用二维数组实例

    在Lua中跟JS也是一样,没有二维数组的概念,但是二维数组对我们的开发又是极其的重要,当然,使用很多啦.谈谈在Lua中如何使用二维数组? 原理,一维数组里面给一个嵌套就是二维数组了,好,就那么简单,看看代码更容易理解 最近在做一个项目,如下是部分代码 for i=1,10 do self.startMartix[i]={} self.star[i]={} for j=1,10 do self.startMartix[i][j]= starSprite self.star[i][j]=star_

  • Lua中的__index方法详解

    当我们访问一个表的不存在的域,返回结果为nil,这是正确的,但并不一定正确.实际上,这种访问触发lua解释器去查找__index metamethod:如果不存在,返回结果为nil:如果存在则由__index metamethod返回结果. 这个例子的原型是一种继承.假设我们想创建一些表来描述窗口.每一个表必须描述窗口的一些参数,比如:位置,大小,颜色风格等等.所有的这些参数都有默认的值,当我们想要创建窗口的时候只需要给出非默认值的参数即可创建我们需要的窗口.第一种方法是,实现一个表的构造器,对

  • Lua中的类编程代码实例

    Lua的类有点像javascript,但是更简明灵活,table即对象,对象就是类.Metatables比起ruby里的MetaClass更加好用,缺点是实例化和继承的代码有点多, 不像ruby里的"<"和"<<",继承链就是查找方法时的方法链. Account={ test1=function(a) print("Account test1") end } Account.test2=function(a) print(&qu

  • Lua中简单的错误处理实例

    昨天遇到另外一位独立游戏开发者,所以多聊了一会,然后-然后就没有看书了.(小若:借口!借口!)   今天来聊聊错误处理吧,不过毕竟这只是前面的章节,书上的内容似乎有点一笔带过的味道. 没关系,简单更好~ 1.红色警报--error 我们应该能经常看到类似以下的错误信息: 复制代码 代码如下: [LUA-print] LUA ERROR: [string "src/main.lua"]:108: [string "src/main.lua"]:89: attempt

  • Lua中遍历文件操作代码实例

    写的一个关于遍历文件的程序段  记录一下咯 --[[检查所有.txt文件 比如A.txt中第一行规定有20列,但是在X行中多输入一个Tab,则输出:A表的X行填写不规范,行末有多余填写 ]] getinfo = io.popen('dir ..//file /b /s') all = getinfo:read('*all') local filenameList = io.open("filename.txt", "wb") filenameList:write(&

随机推荐