Lua函数中传递对象总是引用self

我正在尝试在Lua中创建一个对象("room"),它具有一个函数来检查其他提供的房间是否与它相交。

我遇到的问题是,无论传递什么参数,它似乎总是与我在函数调用的对象相同。

Room = {
    Width = 0,
    Height = 0,
    X = 0,
    Left = 0,
    Right = 0,
    Top = 0,
    Bottom = 0
}

function Room:new(width, height, x, y)
    self.Width = width
    self.Height = height
    self.X = x
    self.Y = y
    self.Left = x
    self.Right = self.X + self.Width
    self.Top = y
    self.Bottom = self.Y + self.Height

    return self
end

function Room:Intersects(other)
    print("Checking for intersection...")
    print("Self X: ", self.X)
    print("Self Y: ", self.Y)
    print("Other X: ", other.X)
    print("Other Y: ", other.Y)
    return other.Left < self.Right and self.Left < other.Right and other.Top < self.Bottom and self.Top < other.Bottom
end

room1 = Room:new(5, 6, 3, 3)
room2 = Room:new(10, 16, 5, 9)

intersects = room1:Intersects(room2)
print("Intersects: ", intersects)

输出:

$ lua FunctionTest.lua
Checking for intersection...
Self X:     5
Self Y:     9
Other X:    5
Other Y:    9
Intersects:     true

我希望self.Xself.Yother.Xother.Y不同。我一直在遵循Lua.org的面向对象编程 章节。

点赞
用户4889587
用户4889587

Room是在顶部定义的单个表格,所有对Room的引用都是作用于同一个表格而不是不同的对象。

下面的解决方案创建了一个名为'obj'的不同表格,并在每次调用NewRoom()时返回此唯一的表格。这种方法与您引用的指南的第16.4章类似。

function NewRoom(width, height, x, y)
    local obj = {
      Width = width,
      Height = height,
      X = x,
      Y = y,
      Left = x,
      Right = x + width,
      Top = y,
      Bottom = y + height,
    }

    function obj:Intersects(other)
      print("Checking for intersection...")
      print("Self X: ", self.X)
      print("Self Y: ", self.Y)
      print("Other X: ", other.X)
      print("Other Y: ", other.Y)
      return other.Left < self.Right and self.Left < other.Right and other.Top < self.Bottom and self.Top < other.Bottom
    end

    return obj
end

room1 = NewRoom(5, 6, 3, 3)
room2 = NewRoom(10, 16, 5, 9)

intersects = room1:Intersects(room2)
print("Intersects: ", intersects)
2019-10-12 18:56:03
用户11298075
用户11298075

如Dahk所说,您定义的Room表也是您返回的“new”Room对象。

“Room:方法(... )”是“Room.method(Room,...)”的语法简写。

Lua提供的“self”变量在使用简写时包含对Room的引用,因此当您键入“Room:new(5、6、3、3)”时,Lua读取的是“Room.new(Room,5、6、3、3)”,其中'self = Room'。然后我们让“self.width = 5”发生的是“Room.width = 5”,这不是我们想要发生的。

为了解决这个问题,每次调用“Room:new”时我们需要创建一个新对象。我们创建一个称为obj的新表,然后在其中存储这些值。

function Room:new(width, height, x, y)
    local obj = {
        Width = width,
        Height = height,
        X = x,
        Y = y,
        Left = x,
        Right = x + width,
        Top = y,
        Bottom = y + height,
    }
    return obj
end

这很有效,我们现在可以创建房间。但是,当我们尝试执行“room1:Intersects(other)”时,我们会收到一个错误:“方法Intersects未定义”。我们可能已经定义了一个新的房间,但这个房间不过是一个简单的表。这就是元表的作用。简而言之,元表定义了表的行为。在我们的情况下,我们希望它包含Lua在原始列表中找不到值或方法时查找的内容。 (有关元表的更多信息,请参见第13章。)

让我们看一个例子:

如果每个房间的地板都相同,我们可以将该地板复制到每个子级中,或者我们让“Room.floor =“ Carpet””,并在有人问我们某个房间的地板时返回该房间。为了返回这个值,我们使用元表和__index方法。这种方法是Lua查找找不到的值的地方。

function Room:new(width, height, x, y)
    local obj = {
        ...
    }
    setmetatable(obj, self) -- 这里的self等于Room
    self.__index = self
    return obj
end

同样,我们创建了具有单独存储每个房间所需的所有值的对象。然后,我们让Room成为obj的元表。第三行告诉我们,在找不到任何方法时应使用Room表。

在创建对象时使用self.__index = self的使用方式与在定义Room本身时写入Room.__index = Room相同,但在每次复制相同/类似代码时会有所帮助 :)

如果我们定义了Room.floor =“ Carpet”,则print(room1.floor)的结果将是所预期的Carpet。room1.floor的值为nil,然后Lua将在room1的元表中查找__index,在那里它发现Room可能包含它正在寻找的内容。确实,Room.floor已定义,因此这是Lua作为答案看到的内容。

总结

您还可以设置元表并使用__index方法定义所需的对象。我认为它可能还更具有内存效率,因为您不必为相同类的每个对象单独定义每个函数。


另外,如果您希望Room:Intersects在执行类似于room1:intersects(room1)之类的操作时返回false,则只需添加self〜= other。仅当selfother是完全相同的表格时才会成功,即使房间的所有值都相同,它们也不会相等,并相互相交。

2019-10-14 08:03:58