如何使Lua对象中的类表成员不同?

考虑以下代码:

#!/usr/bin/lua

local field =
{
    name = '',
    array = {},

    new = function(self, o)
        o = o or {}
        setmetatable(o, self)
        self.__index = self
        return o
    end,
}

local fld1 = field:new()
local fld2 = field:new()

fld1.name = 'hello'
table.insert(fld1.array, 1)
table.insert(fld1.array, 2)
table.insert(fld1.array, 3)

fld2.name = 'me'
table.insert(fld2.array, 4)
table.insert(fld2.array, 5)

print('fld1: name='..fld1.name..' len='..#fld1.array)
print('fld2: name='..fld2.name..' len='..#fld2.array)

执行时的输出如下:

fld1: name=hello len=5
fld2: name=me len=5

从输出中可以看出,在fld1fld2中,name值是不同的。然而,array的值在fld1fld2中是相同的(fld1.arrayfld2.array是相同的,因此长度都是5)。

我该如何修复这个代码,使得fld1.arrayfld2.array独立(使得修改fld1.array不会改变fld2.array)?

点赞
用户2505965
用户2505965

首先,fld1fld2 有不同的名称,因为你为它们分配了不同的名称——它们有自己的属性。

当你执行表键分配时,新的键和值直接存在你指定的表中。__index 元方法只在你查找表键且键未在表中找到时发挥作用。

以下是一个快速示例,我们可以看到表键分配将会在 __index 查找链中 隐藏 键。

local Foo = { shared = 'shared', private = 'private' }
Foo.__index = Foo

local foo = setmetatable({}, Foo)

foo.private = 'a shadowed value'

print(Foo.shared, foo.shared) -- shared shared
print(Foo.private, foo.private) -- private  a shadowed value

注意:有一个 __newindex 元方法可捕获涉及尚未见过键的表键分配。

考虑将 new 方法更像是一个传统构造函数,其中你将“私有”属性分配给新创建的 _实例_。

local Field = {
    -- 共享属性在此处
}

-- 共享方法是这样定义的

function Field:new (name)
    local o = {
        -- 新创建的对象的私有属性在此处
        name = name or '',
        array = {}
    }

    self.__index = self

    return setmetatable(o, self)
end

function Field:insert (value)
    table.insert(self.array, value)
end

local fld1 = Field:new('hello')
local fld2 = Field:new('me')

fld1:insert(1)
fld1:insert(2)
fld1:insert(3)

fld2:insert(4)
fld2:insert(5)

print('fld1: name='..fld1.name..' len='..#fld1.array) -- fld1: name=hello len=3
print('fld2: name='..fld2.name..' len='..#fld2.array) -- fld2: name=me len=2

一些注意事项:

  • 通常,“类”名应在 PascalCase 中,以使它们区分开来。
  • 在查找链中有一个 :new 函数意味着 实例 可以调用它,这可能是可取的也可能不是(通过这种方式创建的继承有点丑)。
  • 对于使用隐式 self 的方法,你应该阅读一下 《Lua 编程》第 16 章。如果你使用的是 5.2 或 5.3,这可能有点过时,但它仍然会有许多有用的信息。
  • 如果你对一个小型库感兴趣,用于此目的,我最近写了一个叫做 Base 的库。如果你仔细寻找,就会发现很多用于使这些事情变得更容易的小型 OOP 包。
2016-08-04 13:17:58
用户4988149
用户4988149

Lua 的面向对象编程技术与 C++、Java 不相同。

__index 元方法引用目标表格记录。

因此,fld1fld2 数组项是 Field 的数组项。

您必须创建一个新表并使用它。

为什么名称的值不同?因为您给一个名称赋值:

fld1.name = 'hello'

简单的代码修正:

fld1.array = {}

使用新方法的代码修正:

local o1 = {name = '', array = {}}
local o2 = {name = '', array = {}}

local fld1 = field:new(o1)
local fld2 = field:new(o2)

创建新表(创建 o)并插入 new 方法。

2016-08-04 14:10:32