停止VBA从调用目标函数两次进行评估

我有麻烦得到VBA的Evaluate()函数只能执行一次; 它似乎总是运行两次。 例如,考虑下面的简单例子。 如果我们运行RunEval()子程序,它将调用EvalTest()函数两次。 这可以通过打印在即时窗口中的两个不同的随机数字来看到。 如果我们使用Evaluate而不是函数调用另一个子例程,行为将是相同的。 有人可以解释我如何得到评估执行目标函数而不是两次? 谢谢。

Sub RunEval() Evaluate "EvalTest()" End Sub Public Function EvalTest() Debug.Print Rnd() End Function 

这个错误似乎只发生在UDF中,而不是内置函数。 你可以通过添加一个expression式来绕过它:

 Sub RunEval() ActiveSheet.Evaluate "0+EvalTest()" End Sub 

但是评估也存在其他一些局限性,这里logging在http://www.decisionmodels.com/calcsecretsh.htm

我做了一个快速search,发现其他人报告了与Application.Evaluate类似的行为和其他奇怪的错误(参见KB823604和this )。 至less在Excel 2002以后,这个问题在微软的名单上可能并不高。这篇知识库文章给出了一个解决方法,可能也适用于你的情况 – 把expression式在工作表中进行评估,然后从就像这样:

 Sub RunEval() Dim d As Double Range("A1").Formula = "=EvalTest()" d = Range("A1").Value Range("A1").Clear Debug.Print d End Sub Public Function EvalTest() As Double Dim d As Double d = Rnd() Debug.Print d EvalTest = d + 1 End Function 

我修改你的例子也返回从函数的随机值。 这将再次打印该值,但添加了一个,所以第二个打印来自第一个子例程。 您可以编写一个支持例程来为任何expression式执行此操作。

我不知道有什么办法可以阻止它,但是你至less可以在大多数情况下认识到这一点。 如果你的计算很费时,或者有不希望发生两次的副作用,并且想要将它短路,那么这可能很有用。

(编辑:查尔斯·威廉姆斯实际上对你的具体问题有一个答案,当你不知道什么数据types你可能会回来,或者当你期望得到像数组或范围的东西时,我的答案仍然可以是有用的。

如果在调用Application.Evaluate的结果中调用的例程中使用了Application.Caller属性,则会看到其中一个调用看起来来自调用Evaluate的实际范围的左上angular单元格来自单元格$ A $ 1和单元格范围内的单元格中的一个。 如果从立即窗口中调用Application.Evaluate ,就像调用示例Sub一样,一个调用看起来来自当前选定范围的左上方单元格,另一个来自当前工作表的单元格$ A $ 1。 我很确定这是第一次打电话,在这两种情况下都是$ A $ 1。 (如果有关系,我会testing一下。)

但是,只有一个值将从Application.Evaluate返回。 我很确定这是第二次评估的结果。 (我也会testing一下。)

显然,这对于实际单元格$ A $ 1所做的调用将不起作用。

(就我而言,我很想知道为什么要进行双重评估,我也很想知道为什么评估者会被暴露出来。

编辑:我问StackOverflow在这里: 为什么Excel的“评估”方法一般的expression式评估?

我希望这有助于,虽然它不直接回答你的问题。

我面临同样的问题,调查后,我发现了两次调用的函数,因为我已经下拉列表和用户定义的函数中使用的值。

通过代码咆哮,将代码放在ThisWorkbook中

Private Sub Workbook_Open()'将计算设置为手动,以在dropdownlist更新时停止计算,并再次计算UDF

  Application.Calculation = xlCalculationManual 

结束小组

Private Sub Workbook_SheetChange(ByVal Sh As Object,_ ByVal Source As Range)

只有在表格改变时才计算

  Calculate 

结束小组

看到没有妥善解决这个问题的办法,我解决了这个问题:

 Dim RunEval as boolean Sub RunEval() RunEval = True Evaluate "EvalTest()" End Sub Public Function EvalTest() if RunEval = true then Debug.Print Rnd() RunEval = False end if End Function 

问题解决了大家。