【问题标题】:Inherit from table returned from function从函数返回的表继承
【发布时间】:2020-04-15 18:51:02
【问题描述】:

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

import api --I don't know how you'd do this in vanilla Lua, I'd use os.loadAPI("api") but that's computercraft specific, I think
Extended = {}
function Extended:foo()
    print("foo from extended")
end

function createExtended(params)
    x = api.createBase(params)
    Extended.__index = x
    return Extended --this is obviously wrong: you can't return a class and expect it to be an object
end

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

e = createExtended()
e.foo() --prints "foo from extended"
e.bar() --nil, therefor error

除了在createExtended 中定义function x.bar() 之外,我怎样才能做到这一点?

提前致谢。

【问题讨论】:

    标签: oop object lua lua-table


    【解决方案1】:

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

    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}
        -- and also `__add`, `__mul`, etc as needed
        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, -- also could be provided via a more complicated __index
       }, {
            __index = function(self, k)
                -- Proxy everything but `newMethod` to `super_instance`.
                local super_field = super_instance[k]
                if type(super_field) == "function" then
                    -- Assume the access is for getting a method, since it's a function.
                    return function(self2, ...)
                        assert(self == self2) -- assume it's being called like a method
                        return super_field(super_instance, ...)
                    end
                end
                return super_field
            end,
            -- similar __newindex and __add, etc. if necessary
        })
    end
    
    local function createExtended(...)
        return extend(createSuper(...))
    end
    

    【讨论】:

    • ooohhhh 我不知道可以做 x.function = alreadyDefinedFunction!太有帮助了,谢谢!
    • function a:b(c, d) ...... end 只是a.b = function(self, c, d) ...... end 的糖!
    • 是的,我从来不知道它似乎非常有用!我知道(我不知道很多)具有这种行为的唯一其他语言是 python:如果我没记错的话,函数只是一种数据类型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多