通过从函数返回的表来继承

有一个API提供的函数,让我们称之为 createBase,它返回一个表(对象)。我想向这个表中添加方法,但我不能只是 x = createBase(),然后 function x:foo(),因为我还有另一个类似 createBase 的函数,但它是 createExtended。为了更好地解释我现有的代码,可能更容易理解:

import api --我不知道你如何在vanilla Lua中实现这一点,我会使用os.loadAPI("api"),但那是特定于computercraft的,我认为
Extended = {}
function Extended:foo()
    print("foo from extended")
end

function createExtended(params)
    x = api.createBase(params)
    Extended.__index = x
    return Extended --这显然是错误的:你不能返回一个类并希望它成为一个对象
end

当然,这不起作用:但我也不知道我如何使它起作用。假设 createBase 返回的表中有一个名为 bar 的函数,它只打印 bar from base。使用此测试代码,将给出以下输出:

e = createExtended()
e.foo() --打印 "foo from extended"
e.bar() --nil,因此出错

除了在 createExtended 中定义 function x.bar(),我如何使这成为可能?

提前致谢。

点赞
用户2616735
用户2616735

最简单的方法是直接将该方法附加到对象上,而不是使用元表。

local function extend(super_instance)
    super_instance.newMethod = newMethod
    return super_instance
end

local function createExtended(...)
    return extend(createSuper(...))
end

这种方法可以正常工作,除非您的超类使用了 __newindex(例如,防止您对未知属性/方法进行写入),或者使用 pairsnext 遍历键,因为它现在具有额外的键。

如果由于某种原因您不能修改对象,则必须对其进行"包装"。


您可以创建一个新实例,将其中所有的方法、属性和运算符“代理”到另一个实例上,除此之外还添加了额外的字段和方法。

local function extend(super_instance)
    local extended_instance = {newMethod = newMethod}
    -- 还可以根据需要添加 `__add`、`__mul` 等
    return setmetatable(extended_instance, {__index = super_instance, __newindex = super_instance})
end

local function createExtended(...)
    return extend(createSuper(...))
end

这对于简单的类来说是可以工作的,但不适用于所有的场景:

pairsnext 这样的表迭代器不会找到原始表的键,因为它们实际上不存在。如果超类检查给定的对象的元表(或者如果超类实际上是一个用户数据),那么它也无法正常工作,因为您会找到扩展元表。

但是,许多纯 Lua 类不会执行这些操作,因此这仍然是一个相当简单的方法,可能适合您的情况。


您还可以像 Go 一样做一些类似的事情;而不是有一种方法来“扩展”类,您只需将该类嵌入为一个字段,并提供一种直接调用包装类上的方法的方便方式,这些方法只是直接调用“扩展”类上的方法。

由于 Lua 中“方法”的工作方式略微复杂,您无法确定某个属性是一个函数属性还是一个实际的方法。下面的代码假定所有的属性都是 type(v) == "function" 的方法,这通常是正确的,但对于您的特定情况可能并不完全正确。

在最坏的情况下,您只需手动维护您要代理的方法/属性列表,但这取决于您需要代理的类数量以及它们拥有的属性数量,这可能会变得不易管理。

local function extend(super_instance)
    return setmetatable({
        newMethod = newMethod, -- 也可以通过更复杂的 __index 提供
   }, {
        __index = function(self, k)
            -- 将除`newMethod`以外的所有内容代理到`super_instance`。
            local super_field = super_instance[k]
            if type(super_field) == "function" then
                -- 假设访问是获取方法,因为它是一个函数。
                return function(self2, ...)
                    assert(self == self2) -- 假设它是像方法一样被调用的
                    return super_field(super_instance, ...)
                end
            end
            return super_field
        end,
        -- 如果需要,还可添加 __newindex 和 __add 等。
    })
end

local function createExtended(...)
    return extend(createSuper(...))
end
2019-12-24 20:24:55