如何在 Lua 中创建一个简单的可导入类?

我想在 Lua 中创建一个名为 MyClass 的类,它存在于一个单独的文件 myclass.lua 中,并且可以导入并在以后使用。 它应该按照以下方式工作:

local MyClass = require 'myclass'
tab = {1,2,3}
m = MyClass(tab)

然而,遵循 Lua 文档中的代码,我无法使它工作并且得到错误 attempt to call global 'MyClass' (a table value)

我到目前为止编写的 myclass.lua 代码如下:

local MyClass = {}
MyClass.__index = MyClass

function MyClass.__init(tab)
    self.tab = tab or {}
    setmetatable({},MyClass)
    return self
end
return MyClass

有很多关于如何在 Lua 中编写类的示例,但我不认为我理解差异,因此在实现细节中迷失了。有没有一种更或者更少“约定俗成”的方法来做到这一点?

点赞
用户1190388
用户1190388

在表中没有 __init 元方法可用。当你执行下面的操作时:

m = MyClass(tab)

它会查找 MyClass.__call 方法定义。只需更新你的 myclass.lua 如下:

local MyClass = {}
MyClass.__index = MyClass

function MyClass:__call(tab)
    self.tab = tab or {}
    setmetatable({},MyClass)
    return self
end

return MyClass
2017-05-22 19:07:02
用户5287638
用户5287638

在 Lua 中,通常不能像调用函数一样调用表。例如,这段代码会产生一个错误:"attempt to call local 't' (a table value)"。

local t = {}
t()

然而,使用元表可以让这个代码正常工作。

local hello = {}
local mt = {} -- 元表
mt.__call = function ()
  print("Hello!")
end
setmetatable(hello, mt)
hello() -- 打印 "Hello!"

当你试图像调用函数一样调用表时,Lua 首先检查表是否有元表。如果有,它尝试调用该元表中的 __call 属性中的函数。 __call 函数的第一个参数是表本身,后续参数是在将表作为函数调用时传递的参数。如果表没有元表或元表没有 __call 函数,则会引发错误 "attempt to call local 't'"。

你的示例代码有三个问题:

  1. 你尝试使用 __init 而不是 __call。Lua 没有 __init 元方法。
  2. __call 接受与你使用的参数不同。 __call 函数的第一个参数是表本身。你可以使用 function MyClass.__call(self, tab),或者使用冒号语法,function MyClass:__call(tab),后者会隐式为你添加 self 参数。这两个语法在功能上是相同的。
  3. 你没有为 MyClass 表设置元表。虽然你为 MyClass 的对象设置了元表,但这并不意味着 MyClass 本身自动设置了元表。

要修复这个问题,你可以像下面这样做:

local MyClass = {}
setmetatable(MyClass, MyClass)
MyClass.__index = MyClass

function MyClass:__call(tab)
    local obj = {}
    obj.tab = tab or {}
    setmetatable(obj, MyClass)
    return obj
end

return MyClass

这将 MyClass 设置为使用自身作为元表,这在 Lua 中是完全合法的。

元表系统非常灵活,允许你拥有任何类/对象方案。例如,如果你想,你可以在内联中完成所有操作。

local MyClass = {}

setmetatable(MyClass, {
    __call = function (class, tab)
        local obj = {}
        obj.tab = tab or {}
        setmetatable(obj, {
            __index = MyClass
        })
        return obj
    end
})

return MyClass

除了简洁之外,这样做还有一个好处,就是如果其他人可以访问类表,他们就不能更改类的元方法。

2017-05-23 04:59:50