如何更改 table 的元 table 但在 Lua 中继承它自己的方法

How to change a table's metatable but inherit it's own methods in Lua

在Lua中,我们是这样进行面向对象编程的:

MyClass = {}

function MyClass:new()
    local obj = {}
    setmetatable(obj, self)
    self.__index = self

    obj.hello = "hello world"

    return obj
end

function MyClass:sayHi()
    print(self.hello)
end

function main()
  local obj = MyClass:new()
  obj:sayHi()
end

当处理更多 compelx 的东西时,通常我利用 Lua 的元方法来代理函数调用并用它做任何我需要的事情,比如参数解析等,通过添加这个:

MyClassMeta = {}

function MyClassMeta.__index(obj, funcName)
    return function (self, ...)
        //do some stuff
        print("you called " .. funcName .. " with args", ...)
    end
end

并更改行:

setmetatable(obj, self)

至:

setmetatable(obj, MyClassMeta)

我用 MyClass 实例调用的每个函数都将执行在 MyClassMeta.__index 元方法中实现的代码。

我现在要做的是继承MyClass已有的方法,只对不属于MyClass.

的函数执行MyClassMeta.__index

在上面的示例中,代码将始终执行MyClassMeta.__index元方法,即使在调用MyClass:sayHi():

时也是如此
function main()
  local obj = MyClass:new()
  obj:sayHi("hello")
end

you called sayHi with args hello

当您将 __index 设置为 table 时,它将在 table 和 return 上查找属性(如果它们不存在于实例中)。由于 sayHi 存在于 MyClass table 上,因此被使用。

self.__index = self

当您将 __index 设置为一个函数时,它可以 return 任何实例上不存在的属性。您可以检查密钥是否存在于 MyClass table 和 return 上,如果不存在则执行其他操作:

MyClass = {}

MyMetatable = {
  __index = function(obj, key)
    if MyClass[key] ~= nil then return MyClass[key] end
    return function(self, ...)
      print("you called "..tostring(key))
      print("  self.hello is '"..tostring(self.hello).."'")
      print("  with args", ...)
    end
  end
}

function MyClass:new()
    local obj = {}
    setmetatable(obj, MyMetatable)

    obj.hello = "hello world"
    return obj
end

function MyClass:sayHi()
    print(self.hello)
end

function main()
  local obj = MyClass:new()
  obj:sayHi()
end

local obj = MyClass:new()
obj:sayHi("hello")
obj:somethingElse(1, 2, 3)

有 Egor 评论的版本

MyClass = {}

setmetatable(MyClass, {
  -- if it's not found on MyClass, return a function
  __index = function(self, funcName)
    return function(self, ...)
      print("you called "..funcName.." with args", ...)
    end
  end
})

function MyClass:new()
  local obj = {}
  -- if it's not found on obj, try self (MyClass)
  setmetatable(obj, { __index = self })
  obj.hello = "hello world"
  return obj
end

function MyClass:sayHi()
    print(self.hello)
end

local obj = MyClass:new()
obj:sayHi()
obj:somethingElse(1, 2, 3)

创建对象时,这会将新对象的元table的__index设置为MyClass,而MyClass的元table的索引是后备。因此,如果 属性 不在您的对象 或 MyClass 上的 上,它将使用回退。