非易失性UDF总是重新计算

use*_*716 6 vba volatile user-defined-functions excel-2010

我试图制作一个非易失性UDF,但似乎不可能.所以这是一个非常简单的测试-UDF:

Option Explicit
Dim i As Integer

Sub Main()

i = 0
[A1] = "zyy"
MsgBox i

End Sub

Function Test(rng As Range)
Application.Volatile (False)

Test = rng.Value
i = i + 1

End Function
Run Code Online (Sandbox Code Playgroud)

我得到了一个空的工作表,它使用了这个函数几次,每次我调用Main()并用UDF​​更改工作表上的任何单元格,所有这些都重新计算.如何使这个(任何)UDF不易变?Application.Volatile(False)应该有这种效果,但显然不起作用.

编辑:如果我手动更改单元格它会按预期工作,它只会在我通过VBA更改单元格时重新计算.这是正常的行为还是可以改变它?

Dav*_*ens 9

我发布了一个新的答案,而不是试图挽救我以前的答案,即使我认为他们指向同一件事,最好重新开始.

背景:

以前我测试过你的代码,如果你只是省略了False那个语句,结果就像我期望的那样.我从未见过任何明确的理由Application.Volatile (False),因为这相当于完​​全省略了陈述.

  • 如果省略,功能是非易失性的和UDF评估当参考变化(即,当其它非指涉细胞改变)
  • 如果包含为Application.Volatile(或者Application.Volatile (True),UDF变得不稳定,对工作表的任何更改都将强制重新评估.

继续调查

您评论说您仍然观察到其他情况.所以我对我的代码做了一些更改并再次测试.突然之间发生了奇怪的事情.无论我对Application.Volatile函数做了什么,对工作表的任何更改都是重新评估UDF.

这没有意义,所以我开始使用Google搜索并进行更多测试.

在我的测试中,我创建了三个函数.

  1. 第一个是明确的易失性:
  2. 第二个显然不易变.
  3. 第三部分省略了任何波动性陈述.

我在工作表上放了每个公式的一个实例.每个引用不同的范围.

在此输入图像描述

我通过更改工作表(手动)和命名子例程来测试每一个.我使用了一个Print语句并监视VBE中的立即窗口,以确认在所有情况下,仅按预期评估(或不评估)函数.第一个总是评估,而2和3仅评估参考范围是否改变.

Function f_appvol(rng As Range)

Application.Volatile
Debug.Print "f_appvol"

f_appvol = rng.Value
End Function

Function f_appNOTvol(rng As Range)
Application.Volatile (False)
Debug.Print "f_appNOTvol"

f_appNOTvol = rng.Value
End Function

Function f_omit(rng As Range)

Debug.Print "f_omit"
f_omit = rng.Value

End Function
Run Code Online (Sandbox Code Playgroud)

然后它变得很奇怪......

我开始在这些功能中进行更改,他们开始表现得很糟糕.

具体来说,我很幸运,并注意到如果我将非易失性函数更改为易失性函数,那么所有函数都开始表现得好像它们是不稳定的 - 甚至是f_omit.我相信这可能是您遇到的情况.

不知何故,我们设法"混淆"Excel

我保存了工作簿并再次尝试......恢复正常!

然后我在volatile语句中更改了参数,并再次发生了奇怪的行为.

这似乎是一个错误

我没有在文档中看到任何暗示这是正常/预期行为的内容,并且从调试的角度来看,它确实不是理想的行为.这就是让你沮丧地拔出头发的东西!

我使用的是Excel 2010,Win 7 64b.

解析度

错误的原因似乎是改变了UDF的波动性.

为了恢复预期的行为,似乎有必要保存工作簿.同样,我不认为这是正常的,但它似乎解决了你的问题(或至少是一个非常类似的问题,我能够在你的故障排除时复制).

在一个可能相关的说明

似乎有至少一个错误有关的波动,提到这里.我链接到它主要是因为这位作者回应了我自己的观点:没有理由这样做,Application.Volatile (False)因为那是(或应该是)UDF的"正常"状态.

我不得不承认我从未见过使用Application.Volatile False的意义,因为如果你Application.Volatile完全省略语句那应该是你得到的.


use*_*716 2

我找到了解决方案,它确实是一个非常简单的解决方案,但也使调试变得困难:如果您对 VBA 代码进行任何更改,所有 UDF 都会被标记为重新计算!我修改了David的调试代码:

Sub main()

'nothing depends on the Value in [A13]
[A13] = "" 
[A13] = "hgdg"
[A13] = ""
i = 46

End Sub

Function f_appvol(rng As Range)

Application.Volatile
Debug.Print "f_appvol"

f_appvol = rng.Value
End Function

Function f_appNOTvol(rng As Range)
Application.Volatile (False)
Debug.Print "f_appNOTvol"

f_appNOTvol = rng.Value
End Function

Function f_omit(rng As Range)

Debug.Print "f_omit"
f_omit = rng.Value

End Function
Run Code Online (Sandbox Code Playgroud)

输入代码并第一次运行后,仅重新计算 f_appvol。如果您现在将 i=46 更改为 i=47 并执行它,所有 UDF 都会重新计算。更改后第一次运行后的所有后续运行都会给出预期的行为。