lua table只读

8.4k 词

function read_only(inputTable)
    local travelled_tables = {}
<span class="kd">local</span> <span class="k">function</span> <span class="nf">__read_only</span><span class="p">(</span><span class="n">tbl</span><span class="p">)</span>
    
    <span class="k">if</span> <span class="ow">not</span> <span class="n">travelled_tables</span><span class="p">[</span><span class="n">tbl</span><span class="p">]</span> <span class="k">then</span>
		<span class="c1">--__read_only_proxy是为了避免重复创建read_only,每个table只创建一个proxy代理,在table的metatable和proxy的metatable里都设置属性__read_only_proxy,其实也是为了有互相引用的情况存在,table有索引指向自身</span>
        <span class="kd">local</span> <span class="n">tbl_mt</span> <span class="o">=</span> <span class="nb">getmetatable</span><span class="p">(</span><span class="n">tbl</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">tbl_mt</span> <span class="k">then</span>
            <span class="n">tbl_mt</span> <span class="o">=</span> <span class="p">{}</span>
            <span class="nb">setmetatable</span><span class="p">(</span><span class="n">tbl</span><span class="p">,</span> <span class="n">tbl_mt</span><span class="p">)</span>
        <span class="k">end</span>

        <span class="kd">local</span> <span class="n">proxy</span> <span class="o">=</span> <span class="n">tbl_mt</span><span class="p">.</span><span class="n">__read_only_proxy</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">proxy</span> <span class="k">then</span>
            <span class="n">proxy</span> <span class="o">=</span> <span class="p">{}</span>
            <span class="n">tbl_mt</span><span class="p">.</span><span class="n">__read_only_proxy</span> <span class="o">=</span> <span class="n">proxy</span>
            <span class="kd">local</span> <span class="n">proxy_mt</span> <span class="o">=</span> <span class="p">{</span>
                <span class="n">__index</span> <span class="o">=</span> <span class="n">tbl</span><span class="p">,</span>   <span class="c1">--对于原表tbl的访问用 __index=tbl</span>
                <span class="n">__newindex</span> <span class="o">=</span> <span class="k">function</span> <span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="nb">error</span><span class="p">(</span><span class="s2">&#34;error write to a read-only table with key = &#34;</span> <span class="o">..</span> <span class="nb">tostring</span><span class="p">(</span><span class="n">k</span><span class="p">))</span> <span class="k">end</span><span class="p">,</span>
                <span class="n">__pairs</span> <span class="o">=</span> <span class="k">function</span> <span class="p">(</span><span class="n">t</span><span class="p">)</span> <span class="k">return</span> <span class="nb">pairs</span><span class="p">(</span><span class="n">tbl</span><span class="p">)</span> <span class="k">end</span><span class="p">,</span>
                <span class="c1">-- __ipairs = function (t) return ipairs(tbl) end,   5.3版本不需要此方法</span>
                <span class="c1">--__len = function (t) return #tbl end,  --lua5.1 版本没有此元方法</span>
                <span class="n">__read_only_proxy</span> <span class="o">=</span> <span class="n">proxy</span>
            <span class="p">}</span>
            <span class="nb">setmetatable</span><span class="p">(</span><span class="n">proxy</span><span class="p">,</span> <span class="n">proxy_mt</span><span class="p">)</span>
        <span class="k">end</span>
        
        <span class="n">travelled_tables</span><span class="p">[</span><span class="n">tbl</span><span class="p">]</span> <span class="o">=</span> <span class="n">proxy</span>
        <span class="c1">--tbl_mt.__read_only_proxy 和 proxy_mt.__read_only_proxy = proxy 跟下面的递归遥相辉映</span>
        <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="k">in</span> <span class="nb">pairs</span><span class="p">(</span><span class="n">tbl</span><span class="p">)</span> <span class="k">do</span>
            <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="o">==</span> <span class="s2">&#34;table&#34;</span> <span class="k">then</span>
                <span class="n">tbl</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">__read_only</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
            <span class="k">end</span>
        <span class="k">end</span>
    <span class="k">end</span>
    <span class="k">return</span> <span class="n">travelled_tables</span><span class="p">[</span><span class="n">tbl</span><span class="p">]</span>
<span class="k">end</span>
<span class="k">return</span> <span class="n">__read_only</span><span class="p">(</span><span class="n">inputTable</span><span class="p">)</span>

end

–[[
–加上travelled_tables是为了防止互相引用,用它来存储已经设置过proxy的table的映射,比如
local t = {a = 1, b = 2}
t.c = t – 有索引指向本身
local tt = read_only(t)
print(tt.a, tt.b, tt.c)
print(tt.c)
print(tt.c.a, tt.c.b)
–输出
–1 2 table: 0039C6A8
–1 2
–如果不用 travelled_tables,将会进入死循环
]]

–[[
function testReadOnly( tbl )
setmetatable(tbl, {
__newindex = function ( tbl, key, value )
print("can not set read only")
end
})
end
local t4 = {a = 1}
testReadOnly(t4)
t4.b = 2
t4.a = 4
print(t4.a, t4.b)–输出 4 nil
–之前以为可以这么设置只读,发现是不行的,对已经存在的字段可以重新赋值
]]