删除不相关的单元格时需要停止重新计算 UDF

Need to stop UDFs recalculating when unrelated cells deleted

我注意到每当我删除单元格时我的 UDF 都会重新计算。这会在删除整个列时造成大量延迟,因为 UDF 会针对使用它的每个单元格调用。因此,如果您使用 1000 个 UDFS,则删除一个列或单元格将调用它 1000 次。

例如,将以下 UDF 放入模块中,然后使用 =HelloWorld()

从工作sheet 中多次调用它
Function HelloWorld()
HelloWorld = "HelloWorld"
Debug.Print Now()
End Function

然后删除一行。如果您的经历和我一样,您会看到它在每次使用时都会被调用一次。

有人知道是否可以阻止这种行为吗?我也很感兴趣为什么要调用它。对我来说,这似乎是 Excel 的依赖树中的一个缺陷,但可能有一个很好的理由。

编辑:经过实验,我发现了更多触发 UDFS 的操作:

  1. ListObject(即 Excel Table)通过调整大小(但 不是 行)。即使 UDF 本身不在相关的 ListObject 中,或者实际上根本不在 any ListObject 中。
    1. 在 sheet 中的任意位置添加新单元格或列(但不包括行)。

请注意,手动计算模式在多个方面都不是一个选项。

首先,考虑到它是一个应用程序级别的设置,它只会带来太大的风险,以至于有人会在没有意识到的情况下使用他们碰巧打开的任何一个传播sheet的输出处于手动计算模式。

其次,我实际上并不是在设计特定的传播sheet,而是在写一本关于非开发人员如何利用编写良好的现成代码(例如 UDF)来做一些事情的书否则会超出他们。示例包括动态连接或拆分文本,或 Charles Williams 在 https://fastexcel.wordpress.com/2011/07/22/developing-faster-lookups-part-2-how-to-build-a-faster-vba-lookup/ 概述的完全匹配二分搜索 UDF(是的,我给了他们很多警告,通常基于本地公式的解决方案将胜过 UDF。但是当你从我上面引用的线程中可以看出,精心编写的函数可以很好地执行)。

我不知道用户将如何使用这些。

在没有编程解决方案的情况下,看来我只需要在书中指出,如果用户使用资源密集型 UDFS,他们在添加或删除单元格或调整 ListObjects 大小时可能会遇到明显的延迟。即使这些 UDF 被有效地编写。

不幸的是,我认为在删除 "unrelated" 个单元格时无法阻止重新计算 UDF。这样做的原因是传递给 UDF 的参数实际上是一个 Range 对象(而不仅仅是单元格中的值)。删除 "unrelated" 个单元格实际上可以修改 Range

比如用户可以写这样的UDF:

Function func1(rng)
    func1 = rng.Address & " (" & Format(Now, "hh:mm:ss") & ")"
End Function

诚然,这不是编写 UDF 的常用(和推荐)方法。它通常应该取决于内容(值)而不是容器(范围)。

这里我只是返回参数的地址。我还附加了一个时间戳以在重新计算 UDF 时发出信号。如果删除工作表上的任何列,将重新计算具有此 UDF 的所有单元格。但是,如果您插入一列,而右侧(该新列的)单元格保持不变且值(单元格地址)错误,则不会。结果与 inserting/deleting 行相同。奇怪的是,插入单个单元格会强制重新计算所有 UDF。

我试图删除 Range 上的 "dependency"。但是,即使 UDF 的参数被键入 as double(而不是像我的示例中那样将其保留为 Variant),行为也是相同的。

正如您所解释的,删除列将强制重新计算 UDF。这是有道理的,因为 UDF can 取决于 Range 参数。这是否是 UDF 的智能设计是另一回事。

插入或删除行或列或单元格将始终触发自动模式下的重新计算。 (您可以通过将 =NOW() 添加到空工作簿并插入或删除内容来检查这一点)


问题应该是什么(意外)情况将单元格标记为脏,以便重新计算。 有一个(可能不完整的)此类事情的清单 http://www.decisionmodels.com/calcsecretsi.htm

看来我需要添加一些关于 VBA UDF 的话(尚未测试 XLL UDF
- 它们的行为可能会有所不同,因为它们以与 VBA 不同的方式注册UDF)