Как изменить метатабельность таблицы, но наследовать ее собственные методы в Lua - PullRequest
0 голосов
/ 28 июня 2018

в 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 и выполнять MyClassMeta.__index только для функций, не являющихся частью MyClass.

В приведенном выше примере код всегда выполняет метаметод MyClassMeta.__index даже при вызове MyClass:sayHi():

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

ты позвонил sayHi с аргументами привет

1 Ответ

0 голосов
/ 03 июля 2018

Когда вы устанавливаете __index для таблицы, она будет искать свойства в этой таблице и возвращать их, если они не существуют в экземпляре. Поскольку sayHi существует в таблице MyClass, она используется.

self.__index = self

Когда вы устанавливаете __index для функции, она может возвращать что угодно для свойств, которые не существуют в экземпляре. Вы можете проверить, существует ли ключ в таблице MyClass и вернуть его, а затем сделать что-то еще, если его нет:

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)

Версия с комментариями Егора

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)

При создании объекта это устанавливает __index метатаблицы нового объекта равным MyClass, а индекс метатаблицы MyClass для функции, которая является резервной. Поэтому, если свойство не находится в вашем объекте или в MyClass, оно будет использовать запасной вариант.

...