lua创建只读table表

lua中设置和创建只读table

首先感谢 Lua 设置table为只读属性
在被人的肩膀上略微做了些修改

使用到的 lua metamethod

  • __index
    当你通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。如果__index包含一个表格,Lua会在表格中查找相应的键。

  • __newindex
    当你给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:如果存在则调用这个函数而不进行赋值操作。
    __newindex 元方法对表更新操作

代码实现

-** 创建只读 table 
-- 允许插入添加新的数据,允许访问已有的数据,当修改以后的数据时会提示error
-- 每个tb只创建一个 proxy, 避免重复设置 read_only
-- travelled_tables 避免表间互相引用
-- proxy 是返回的 read_only_table, 其永远保持空表,使得每次赋值都可以调用__newindex
-- lua5.3支持 table.remove 抛出错误,
-- 支持 pairs ipairs 迭代遍历, 不支持 next访问 
-- 用法: 
--      local tb = CreateReadonlyTable()
--      tb.a = 1
--      table.insert(tb, 2)
--      table.tt = {a = 1}
*]]

local function CreateReadonlyTable()
    local readonly_table = {}
    local travelled_tables = {}

    local function __read_only(tb)
        if travelled_tables[tb] then
            return travelled_tables[tb]
        end

        local tb_mt = getmetatable(tb)
        if tb_mt == nil then
            tb_mt = {}
            setmetatable(tb, tb_mt)
        end         

        local proxy = tb_mt.__read_only_proxy    
        if not proxy then
            proxy = {}
            tb_mt.__read_only_proxy = proxy
            
            local proxy_mt = {}
            proxy_mt.__index = tb
            proxy_mt.__newindex = function(t, key, value)            
                local gain_value = rawget(tb, key)
                if gain_value ~= nil then
                    error("error write to a read-only table with key =".. tostring(k))
                    return
                end

                if type(value) == "table" then                   
                    rawset(tb, key,  __read_only(value))
                else
                    rawset(tb, key, value)
                end                
            end
            proxy_mt.__pairs = function(t)
                return pairs(tb)
            end
            proxy_mt.__ipairs = function(t)
                return ipairs(tb)
            end
            proxy_mt.__len = function(t)
                return #tb
            end
            proxy_mt.__read_only_proxy = proxy

            setmetatable(proxy, proxy_mt)
        end

        travelled_tables[tb] = proxy
        return proxy
    end

    return __read_only(readonly_table)
end
  1. 创建并返回空表 proxy, 重写 __newindex 使其一直保持空表形式。使得赋值操作都会触发其 __newindex元方法。新插入的数据被添加到readonly_table表中
  2. __index 从readonly_table 表中取出已有数据
  3. __read_only_proxy 是为避免重复的read_only 设置
  4. travelled_tables 为避免表之间的互相引用

在贴上大牛的原创

--[[------------------------------------------------------------------------------
-** 设置table只读 
-- 出现改写会抛出lua error
-- 用法 local cfg_proxy = read_only(cfg) retur cfg_proxy
-- 增加了防重置设置read_only的机制
-- lua5.3支持 1)table库支持调用元方法,所以table.remove table.insert 也会抛出错误,
--  2)不用定义__ipairs 5.3 ipairs迭代器支持访问元方法__index,pairs迭代器next不支持故需要元方法__pairs
-- 低版本lua此函数不能完全按照预期工作
*]]

function read_only(inputTable)
    local travelled_tables = {}

    local function __read_only(tbl)
        if not travelled_tables[tbl] then
            local tbl_mt = getmetatable(tbl)
            if not tbl_mt then
                tbl_mt = {}
                setmetatable(tbl, tbl_mt)
            end
    
            local proxy = tbl_mt.__read_only_proxy
            if not proxy then
                proxy = {}
                tbl_mt.__read_only_proxy = proxy
                local proxy_mt = {
                    __index = tbl,
                    __newindex = function (t, k, v) error("error write to a read-only table with key = " .. tostring(k)) end,
                    __pairs = function (t) return pairs(tbl) end,
                    -- __ipairs = function (t) return ipairs(tbl) end, 5.3版本不需要此方法
                    __len = function (t) return #tbl end,
                    __read_only_proxy = proxy
                }
                setmetatable(proxy, proxy_mt)
            end

            travelled_tables[tbl] = proxy
            for k, v in pairs(tbl) do

                if type(v) == "table" then
                    tbl[k] = __read_only(v)
                end
            end
        end
        return travelled_tables[tbl]

    end
    return __read_only(inputTable)
end
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值