尝试使用 metatables 创建 lua 代理

我阅读了有关 metatables 的文章 (这里这里),我想知道是否可以通过将一个与索引函数同名的函数添加到 __index 表中来创建函数调用代理,这样它就调用了索引函数(我的函数),然后我可以调用普通的函数。

这是我尝试过的:

local test = {}
test.static = function () print("static") end -- 正常函数

local old = getmetatable(test) or {} -- 获取原始元表
old.__index = {
    static = function () print("hook") end -- 添加我的函数
}

test.static() -- 仅用于测试 ( 输出 -> "static" )
test = setmetatable( test , old ) -- 设置元表
test.static() -- 仅用于测试 ( 输出 -> "static",但应该是 "hook" )
点赞
用户2505965
用户2505965

尝试阅读官方来源,特别是§2.4-元表和元方法,以及__index元方法的描述,如下所示:

  • __index:索引访问table[key]。当table不是表或key不存在于table中时发生此事件。在table中查找元方法。

    尽管名称如此,但此事件的元方法可以是函数或表。如果它是函数,则将其作为参数调用with中和key,调用的结果(调整为一个值)是操作的结果。如果它是一个表,则最终结果是使用key对此表进行索引的结果。(这种索引是常规的,不是原始的,因此可能会触发另一个元方法。)

我们可以看到你的想法是反向的。仅当原始表不包含键时才会查找由__index元方法引用的表中的属性。

如果您想要“挂钩”函数,则需要覆盖它,可能会保存原始函数以便以后恢复。如果您想要在现有函数上添加功能,可以编写一个漂亮的小钩子函数,它只是简单地创建一个环绕函数,并依次调用它们。

local function hook (original_fn, new_fn)
    return function (...)
        original_fn(...)
        new_fn(...)
    end
end

local test = {}
test.foo = function () print('hello') end
test.foo = hook(test.foo, function () print('world!') end)

test.foo() -->打印"hello"然后"world!"

或者,您可以在元表之间切换,假设原始表从不覆盖感兴趣的键,以获得不同的结果:

local my_table, default, extra = {}, {}, {}

function default.foo () print('hello') end

function extra.foo() print('world!') end

local function set_mt (t, mt)
    mt.__index = mt
    return setmetatable(t, mt)
end

set_mt(my_table, default)
my_table.foo() -->打印'hello'
set_mt(my_table, extra)
my_table.foo() -->打印'world!'
2016-11-03 02:59:16