【问题标题】:Need to stop UDFs recalculating when unrelated cells deleted删除不相关的单元格时需要停止重新计算 UDF
【发布时间】:2015-07-01 03:34:33
【问题描述】:

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

例如,将以下 UDF 放入模块中,然后使用 =HelloWorld() 从工作表中多次调用它

Function HelloWorld()
HelloWorld = "HelloWorld"
Debug.Print Now()
End Function

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

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

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

  1. ListObject(即 Excel 表格)通过调整大小(但不是行)跨越的列数的任何更改。即使 UDF 本身不在相关的 ListObject 中,或者实际上根本不在 any ListObject 中。
    1. 在工作表的任意位置(但不是行)添加新的单元格或列。

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

首先,鉴于这是一个应用程序级别的设置,它只会带来太大的风险,即有人会使用他们碰巧打开的任何一个电子表格的输出而没有意识到它们处于手动计算模式。

其次,我实际上并不是在设计一个特定的电子表格,而是在写一本关于非开发人员如何利用编写良好的现成代码(例如 UDF)来完成原本超出他们能力范围的事情的书。示例包括动态连接或拆分文本,或查尔斯·威廉姆斯在https://fastexcel.wordpress.com/2011/07/22/developing-faster-lookups-part-2-how-to-build-a-faster-vba-lookup/ 概述的精确匹配二进制搜索 UDF(是的,我给他们很多警告,通常基于本机公式的解决方案将胜过 UDF。但正如您将从我上面引用的线程看,精心编写的函数可以很好地执行)。

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

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

【问题讨论】:

  • dicks 博客遇到了 stackoverflow :) 有趣的问题,因为我确实证实了这一点,即使您的 UDF 没有关联的单元格范围、与其结合的 volatile 函数或 Application.Volatile。一个简单的清除内容不会触发 UDF,删除行会。我会感兴趣地等待查尔斯·威廉姆斯的帖子。
  • 您是否尝试将excel计算方法设置为手动。手动计算模式意味着 Excel 只会在您通过按 F9 或 Ctrl-Alt-F9 请求或保存工作簿时重新计算所有打开的工作簿。对于花费超过几分之一秒重新计算的工作簿,通常最好将计算设置为手动。
  • 嗨,Izzymo。由于以下原因,我会尽可能避开手动计算模式: * 可能会在未刷新模型的情况下使用输出。我通常不是唯一的用户。 * 如果您在白天打开的第一个工作簿恰好是在手动计算模式下保存的工作簿,那么您在白天打开的所有其他工作簿也将切换到手动计算模式。可能在你不知情的情况下。通常使用手动计算模式来掩盖设计不佳的电子表格的症状。并非在所有情况下,我也不是说这一切都很糟糕。基本上是可以避免的。

标签: excel vba volatility udf


【解决方案1】:

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


问题应该是什么(意外)情况将单元格标记为脏,以便重新计算。 在 http://www.decisionmodels.com/calcsecretsi.htm

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

【讨论】:

    【解决方案2】:

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

    例如,用户可以编写这种 UDF:

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

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

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

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

    正如您所解释的,删除列将强制重新计算 UDF。这是有道理的,因为 UDF 可以 依赖于 Range 参数。对于 UDF 来说,这是否是一个聪明的设计是另一回事。

    【讨论】:

    • re your comment **原因是传递给 UDF 的参数实际上是 Range **:我的简单函数根本不传递任何参数。所以我不明白为什么 Excel 需要重新计算它。
    • @jeffreyweir:是的,你是对的。我的意思是 UDF 可能取决于 Range 参数。似乎开发人员选择(不幸的是)更新所有 UDF,而不是仅更新带有 Range 参数的 UDF(即那些可能需要在列删除后更新的 UDF)。
    猜你喜欢
    • 1970-01-01
    • 2013-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-24
    • 1970-01-01
    • 2017-07-06
    • 2011-01-03
    相关资源
    最近更新 更多