深度复制函数变量给出了意想不到的结果

deepcopying function variable gives unexpected results

我有一个使用以下函数创建的对象

local function newObject(functionVariable)
    ...
    functionVariable = functionVariable or nop
    ...
    return setmetatable({
        ...
        functionVariable         = functionVariable,
        ...
    }, Objectmt)
end

当我使用这个深度复制这个对象时

local function deepcopy(t)
    if t == nil then return nil end

    if type(t) == 'table' then
            local copy = {}

            for k, v in pairs(t) do
                    copy[k] = deepcopy(v)
            end
            setmetatable(copy, deepcopy(getmetatable(t)))
            return copy
    else -- number, string, boolean, etc
            return t
    end
end

并使用这个

加载对象
for k, v in pairs(state.objectsTable) do objectsTable[k] = v end

函数变量完全错误。它不再是传递给对象的函数并产生意想不到的结果

有很多地方可能会出错,而且没有足够的信息来确定。但是,我看到有些地方可能与您的副本有问题,尤其是看起来有问题的地方。

您的 deepcopy 函数将复制对象的元table:

-- snip...
setmetatable(copy, deepcopy(getmetatable(t)))
-- ...

但是,相同类型的对象共享相同的元是一种相当普遍的做法table,而且,您的代码似乎是这样做的(尽管没有看到 Objectmt 是如何定义的,它是'清楚)。例如,其他代码可以通过执行以下操作来确定某物是否为对象:

function isObject(obj)
  return getmetatable(obj)==Objectmt
end

您的副本将失败,因为元table 不再相同(即使它具有相同的内容)。

这可以通过使用不同版本的 deepcopy(或修改现有版本)重用元数据来解决table:

-- snip...
setmetatable(copy, getmetatable(t))
-- ...

如果这不是问题,那么还需要考虑一些其他事项:

  • 您的深度复制功能不会复制 table 键。在某些情况下,复制密钥可能很重要,但对于您显示的任何代码而言,情况似乎并非如此。
  • 你的deepcopy函数不会复制函数。如果您使用具有 mutable upvalues 的函数,这可能很重要。同样,您显示的任何代码都不是这种情况。
  • 除了 metatable 之外,可能还有其他 table 应该通过引用而非值进行复制。要知道哪个合适,你必须了解它们的使用方式。
  • 可能存在当前通过引用复制的用户数据或其他不太常见的类型(例如协程),需要通过值进行复制。有些东西可能无法按值复制。
  • 您正在复制的数据中可能存在根本不应该复制的内容,例如唯一标识符。
  • 问题可能根本不在于副本。