在Lua中,你能从'self'获取对象名称吗?

我可能忽略了一些非常简单的东西,但是在Lua中调用对象上的方法时,有没有办法从self变量中获取该对象的名称?

为了说明我的意思,这里是一个有点牵强附会的例子 - 一个简单的堆栈实现:

Stack = {}
function Stack:new (...)
    instance = {}
    instance.elements = {...}
    setmetatable(instance, self)
    self.__index = self
    return instance
end
function Stack:push (...)
    for i,v in pairs({...}) do
        table.insert(self.elements,v)
    end
end
function Stack:pop ()
    if #self.elements > 0 then
        return table.remove(self.elements)
    else
        error("Cannot pop, Stack is empty")
    end
end

my_stack = Stack:new(2,4)
my_stack:push("dog")      -- my_stack.elements = {2,4,"dog"}
print(my_stack:pop())     -- "dog"
print(my_stack:pop())     -- "4"
print(my_stack:pop())     -- "2"
print(my_stack:pop())     -- error: "Cannot pop, Stack is empty"

是否可以在Stack:pop错误消息中使用self变量来输出调用它的对象的名称?我希望错误消息说“ Cannot pop, Stack 'my_stack' is empty”。

点赞
用户869951
用户869951

在 Lua 中,对象没有名字。只有引用有名字,变量和表字段最终只是对对象的引用。所以,如果您想知道如何从self变量中获取引用的名称,显然是无法的,因为可能有任意数量的引用具有不同的名称,那么"self.getName()"会返回哪一个名称呢?

您可以在构造时为每个对象分配唯一标识符。然后,new()的一个参数将是对象名称,您将把它保存为self.name(例如),然后您可以根据需要访问它(我想象一下,用于记录消息或在某些关联数组中进行查找)。所以您会这样做

my_stack = Stack:new("my_stack", 2,4)
my_stack:push("dog")      -- my_stack.elements = {2,4,"dog"}

我不知道有没有办法让new()发现新实例将被分配给变量my_stack。实际上,我甚至怀疑即使是使用调试模块也是不可能的,因为分配只会在new返回后发生。因此,没有任何东西强制对象的名称与变量相同,这完全取决于您自己。这甚至不是您想要的东西,因为,再次提醒一下,您可能有对同一堆栈对象的多个引用:

a = Stack:new("a", 2,4)
b = a  -- b和a在内存中是同一个对象

错误消息应该提到a还是b?我认为更好的方式是为对象分配一个ID,然后您始终知道出现错误消息所涉及的对象:

a = Stack:new("my_stack_1", 2,4)
b = a
t = { b = { c = a } }
a:push("dog") -- 错误将提到my_stack_1:明确您正在引用哪个对象
t.b.c:push("dog") -- 错误将提到my_stack_1:仍然明确您正在引用哪个对象

如果您担心无法从错误消息中确定哪行调用new(self, "dog"),那么debug模块可能会很有用:使用它,您可以获取调用所在的行号和文件,因此您可以说"对象my_stack_1:pop(): 栈中没有元素了(由文件Y的第X行调用)"

2014-04-12 17:53:48
用户1847592
用户1847592
local function get_my_self_name(self)
   local i = debug.getinfo(3, 'Sl');
   return "'"..(io.open(i.source:match'@(.*)'):read'*a'
      :gsub('.-\n','',i.currentline-1)
      :match('(.-)[:.]%s*'..debug.getinfo(2,'n').name..'%s*%(')
      :match'([%w_]+)%s*$' or '<unnamed>').."' at line #"..i.currentline
end

local Stack = {}
function Stack:new (...)
   local instance = {}
   instance.elements = {...}
   setmetatable(instance, self)
   self.__index = self
   return instance
end

function Stack:push (...)
   for i,v in ipairs({...}) do
      table.insert(self.elements,v)
   end
end

function Stack:pop ()
   if #self.elements > 0 then
      return table.remove(self.elements)
   else
      error("Can't pop, Stack "..get_my_self_name(self).." is empty")
   end
end

local my_stack = Stack:new(2,4)
my_stack:push("dog")  --my_stack.elements = {2,4,"dog"}
print(my_stack:pop()) --"dog"
print(my_stack:pop()) --"4"
print(my_stack:pop()) --"2"
print(my_stack:pop()) --error: Can't pop, Stack 'my_stack' at line #35 is empty
2014-04-12 18:50:08