非易失性UDF总是重新计算

时间:2014-06-22 16:54:27

标签: vba excel-2010 user-defined-functions volatile

我正在尝试制作一个非易失性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

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

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

2 个答案:

答案 0 :(得分:9)

我发布了一个新答案,而不是试图挽救我以前的答案,尽管我认为他们指的是同样的事情,但最好重新开始。

<强>背景

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

  • 如果省略,则该函数是非易失性的,当参考发生变化时,UDF仅评估 <( ,当其他非参照单元格发生变化时)
  • 如果包含为Application.Volatile(或Application.Volatile (True),则UDF会变得不稳定并且对工作表进行任何更改 将强制重新评估。

继续调查

你评论说你仍然没有注意到。所以我对我的代码做了一些更改并再次测试。突然之间发生了奇怪的事情。无论我使用Application.Volatile函数做了什么,对工作表的任何更改都会重新评估UDF。

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

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

  1. 第一个是明确的易变性:
  2. 第二个明确不易变
  3. 第三个省略了任何波动性声明。
  4. 我在工作表上放了每个公式的一个实例。每个引用不同的范围。

    enter image description here

    我通过更改工作表(手动)和命名子例程来测试每一个。我使用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
    

    然后很奇怪......

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

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

    不知怎的,我们已经成功地混淆了#34; Excel中

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

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

    这似乎是一个错误

    我没有看到任何表明这是正常/预期行为的in the documentation,并且从调试的角度来看,它确实不是理想的行为。这就是让你沮丧地拔出头发的东西!

    我正在使用Excel 2010,Win 7 64b。

    解决

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

    为了恢复预期的行为,似乎需要保存工作簿。同样,我不认为这是正常的,但它似乎可以解决您的问题(或者至少是一个非常类似的问题,我可以在解决您的问题时进行复制)。

    关于可能相关的说明

    如上所述here,似乎至少有一个与波动有关的错误。我链接到它主要是因为这位作者回应了我自己的观点:没有理由做Application.Volatile (False),因为那是(或应该)&#34;正常&#34; UDF的状态。

      

    我必须承认,我从未见过使用Application.Volatile False的观点,因为如果你完全省略Application.Volatile语句,那就应该是你得到的。

答案 1 :(得分:2)

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

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

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