如何在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

当处理更复杂的东西时,我通常利用Lua的元方法来代理函数调用并根据需要执行任何操作,例如参数解析等,通过添加以下内容:

MyClassMeta = {}

function MyClassMeta.__index(obj, funcName)
    return function (self, ...)
        //执行一些操作
        print("你调用了" .. funcName .. ",参数为:", ...)
    end
end

并将以下代码行更改为:

setmetatable(obj, self)

to:

setmetatable(obj, MyClassMeta)

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

现在我想继承MyClass现有的方法,并仅对不属于MyClass的函数执行MyClassMeta.__index

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

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

你调用了 sayHi,参数为: hello

点赞
用户369792
用户369792

当你将__index设为一个表(table)时,它会在那个表上查找属性值并返回,如果实例没有它们的话。由于sayHi存在于MyClass表中,所以会被使用。

self.__index = self

当你将__index设为一个函数时,它可以对那些实例中找不到的属性值返回任何值。你可以检查MyClass表中是否存在该键(key),如果存在则返回它,否则执行其他操作:

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, {
  -- 如果在MyClass中没有找到,返回一个函数
  __index = function(self, funcName)
    return function(self, ...)
      print("you called "..funcName.." with args", ...)
    end
  end
})

function MyClass:new()
  local obj = {}
  -- 如果在实例(obj)中没有找到,尝试使用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)

当创建一个对象时,它会将新对象的元表(metatable)的__index设为MyClass,而MyClass的元表的__index设为那个函数。所以如果该属性值不在你的对象或MyClass中,它会使用那个函数作为默认值。

2018-07-03 02:04:59