尝试调用一个空值(方法 '...'),如果没有在元表中实现__index

Lua没有提供一种独特的面向对象编程方式。

使用setmetatable有很多替代方案。

这是我尝试的方式:

Person={}

function Person.__call(cls,name)
    return setmetatable({name=name},cls)
end

function Person:say(what)
    print(self.name..'> '..what)
end

setmetatable(Person,Person)

p=Person('Fred')
p:say('hello') -- 18

这将产生以下错误:

18: 尝试调用一个空值(方法 'say')

我可以添加:

function Person.__index(cls,k)
    return Person[k]
end

然后上述代码就可以正确地工作了,但我不明白为什么当Person已经是其自身的元表时,该方法却找不到。

点赞
用户2858170
用户2858170

你需要实现 __index 元值以便中继索引访问操作。仅有元表是不足够的。

同时请注意,在使用表作为元表之前,推荐实现所有元方法。

请参考 Lua 5.4 参考手册 2.4 Metatables and Metamethods

__index: 索引访问操作 table[key]。当 table 不是一个 table 时或者 key 不在 table 中时发生此事件。查找元值在 table 的元表中进行

此事件的元值可以是函数、表或任何带有 __index 元值的值。如果是函数,则将以 table 和 key 作为参数调用它,调用结果(调整为一个值)是操作的结果。否则,最终结果是使用 key 对此元值进行索引的结果。这种索引是常规索引,而不是原始索引,因此可能会触发另一个 __index 元值。

__index 就是那个元值。因此,如果你没有提供该元值,Lua 应该怎么做呢?

在下面的示例中,该元值是 Person。因此,当我调用 a:sayName() 时,Lua 会发现 a.sayName 为 nil。它将检查在 a 的元表 Person 中是否有 __index 元值。在这种情况下,有一个名为 Person 的表,因此它将使用键 "sayName" 索引该 Person,结果是以下函数调用:Person["sayName"](a)

local Person = {}
Person.__index = Person

setmetatable(Person, {
    __call = function (cls, ...)
        return cls:_init(...)
    end,
})

function Person:_init(name)
    local o = setmetatable({}, self)
    o.name = name
    return o
end

function Person:sayName()
    print(self.name)
end

local a = Person("Lisa")
a:sayName()
2021-05-06 08:36:19