首页 > 代码库 > chapter 13_4 跟踪table的访问

chapter 13_4 跟踪table的访问

  __index和__newindex都是在table中没有所需访问的index时才发挥作用。

因此,只有将一个table保持为空,才有可能捕捉到所有对它的访问。为了监视一个table的所有访问,就应该为真正的table创建一个代理。

这个代理就是一个空的table,其中__index和__newindex元方法可用于跟踪所有的访问,并将访问重定向到原来的table上。

假设我们要跟踪table t 的访问,可以这样做:

t  = {}          --original table, 在其他地方创建的local _t = t     --保持对原table的一个私有访问t = {}           --创建一个代理local mt = {            __index = function (t,k)                        print("*access to elemet " .. tostring(k))                        return _t[k]      --访问原table中的k                    end            __newindex = function(t,k,v)                        print("*update of element" .. tostring(k) .. " to " .. tostring(v))                        _t[k] = v        --更新原table的值                    end}setmetatable(t,mt)    --将mt设置为 t 的元表

这段代码跟踪了所有对 table t 的访问:

t[2] = "hello"              -- *update of element 2 to helloprint(t[2])       --> hello        -- *access to element 2

但是上面的例子有一个问题,就是无法遍历原来的table。函数pairs只能操作代理table,而无法访问原来的table。

可以通过定义__pairs去遍历:

mt.__pairs = function()    return function(_,k)        return next(_t,k)        endend

  如果想要同时监视几个table,无须为每个table创建不同的元表。相反,只要以某种形式将每个代理与其原table关联起来,

并且所有代理都共享一个公共的元表。这个问题与前一节所讨论的将table与其默认值相关联的问题类似。

例如将原来table保存在代理table的一个特殊字段中,如下:

local index = {}        --创建私有索引local mt = {            --创建元表    __index = function(t,k)            print("*access to element " .. tostring(k)            return t[index][k]    --访问原来的table        end    __newindex = function(t,k,v)            print("*update of element " .. tostring(k) .. " to " .. tostring(v))            t[index][k] = v        --更新原来的table        end    __pairs = function(t)            return function(t,k)                return next(t[index],k)            end , t        end}function track(t)    local proxy= {}    proxy[index] = t    setmetatable(proxy,mt)    return proxyend

现在,若要监视table t ,唯一要做的就是执行:

t = track(t)

只读的table

  通过代理的概念,可以很容易地就实现出只读的table,只需跟踪所有对table的跟新操作,并引发一个错误就可以了。

由于无须跟踪查询访问,所以对于__index元方法可以直接使用原table来代替函数。这也更简单,并且在重定向所有查询到原table时效率也更高。

不过,这种做法要求为每个只读代理创建一个新的元表,其中__index指向原来的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 proxyend

下面是一个使用的示例,创建了一个表示星期的只读table:

days = readOnly{"Sunday","Monday","Tuesday","Thursday","Friday","Saturday"}print(days[1])        -->Sundaydays[2] = "Noday"     -- > stdin:1: attempt to update a read-only table

以上内容来自:《Lua程序设计第二版》和《Programming in Lua  third edition 》

 

chapter 13_4 跟踪table的访问