有一个提供的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
如果没有在function x.bar()
中定义createExtended
,我怎么能做到这一点?
提前感谢。
最简单的方法是将方法直接附加到它,而不是使用一个元表。
local function extend(super_instance)
super_instance.newMethod = newMethod
return super_instance
end
local function createExtended(...)
return extend(createSuper(...))
end
这将起作用,除非您的超类使用__newindex
(例如,防止您写入未知的属性/方法),或者使用pairs
或next
遍历键,因为它现在将具有一个附加键。
如果由于某种原因无法修改对象,则必须将其“包装”起来。
您可以创建一个新实例,将其所有方法,属性和运算符“代理”到另一个实例,除了它添加了其他字段和方法。
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
这将适用于简单的类,但不适用于所有用途:
pairs
和next
之类的表迭代将无法从原始表中找到键,因为它们实际上并不存在。如果超类检查给定对象的元表(或者超类实际上是用户数据),它也将不起作用,因为您会找到扩展元表。
但是,许多纯Lua类不会执行这些操作,因此,这仍然是一种相当简单的方法,可能对您有用。
您还可以执行类似于Go的操作;无需使用“扩展”类的方法,您只需将该类作为字段嵌入,并提供便利,以便直接在包装类上调用仅在“ extended”类上调用方法的方法。
这对于Lua中“方法”的工作方式有些复杂。您无法确定属性是功能就是属性还是实际上是方法。下面的代码假定type(v) == "function"
的所有属性实际上都是方法,通常为true,但实际上并非针对您的特定情况。
在最坏的情况下,您可以手动维护要“代理”的方法/属性的列表,但是根据需要代理的类数和它们具有的属性,可能会变得笨拙。
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