Excel UDF 将评估的 SUB 的值加倍
Posted
技术标签:
【中文标题】Excel UDF 将评估的 SUB 的值加倍【英文标题】:Excel UDF doubles value of the evaluated SUB 【发布时间】:2017-08-08 09:24:59 【问题描述】:1. 我试图回答 VBA UDF to split string array,但在计算我的 UDF 时得到了令人不快的结果。
Public Function mytest(src, dest)
dest.Parent.Evaluate "test(" & src.Address(False, False) & ", " & dest.Address(False, False) & ")"
mytest = "wut"
End Function
Sub test(src As Range, dest As Range)
Dim chr, rows, cols
rows = 0
cols = 0
For chr = 1 To Len(src.Value)
Select Case Mid(src.Value, chr, 1)
Case ","
rows = rows + 1
Case ";"
cols = cols + 1
rows = 0
Case Else
Cells(dest.Row + rows, dest.Column + cols).Value = Cells(dest.Row + rows, dest.Column + cols).Value & Mid(src.Value, chr, 1) '
End Select
Next chr
End Sub
预期结果: 公式结果: 有人可以解释为什么它会使单元格的值加倍吗? 当我使用
调试 testSub ffs()
Call test(Cells(1, 1), Cells(3, 1))
End Sub
我得到了预期的结果,所以我猜问题不在 test Sub?..
2. 每当我尝试向 Function 和 Sub(例如分隔符)添加更多参数时,Function 根本不会评估 Sub
Public Function CellToRange(src, dest, DelimL, DelimC)
dest.Parent.Evaluate "test(" & src.Address(False, False) & ", " & dest.Address(False, False) & ", " & DelimL & ", " & DelimC & ")"
CellToRange = "wut"
End Function
Sub CTR(src As Range, dest As Range, Delim1, Delim2)
Dim chr, rows, cols
rows = 0
cols = 0
For chr = 1 To Len(src.Value)
Select Case Mid(src.Value, chr, 1)
Case Delim1
rows = rows + 1
Case Delim2
cols = cols + 1
rows = 0
Case Else
Cells(dest.Row + rows, dest.Column + cols).Value = Cells(dest.Row + rows, dest.Column + cols).Value & Mid(src.Value, chr, 1) '
End Select
Next chr
End Sub
请帮助._。并提前致谢。解决方案: 谢谢比利和查尔斯·威廉姆斯。 改变
dest.Parent.Evaluate "CTR(" & src.Address(False, False) & ", " & dest.Address(False, False) & ", " & DelimL & ", " & DelimC & ")"
到
dest.Parent.Evaluate "0+CTR(" & src.Address(False, False) & ", " & dest.Address(False, False) & ", " & DelimL & ", " & DelimC & ")"
谢谢大家!
【问题讨论】:
Stop VBA Evaluate from calling target function twice的可能重复 @Billy That.. 实际上解决了我的问题 【参考方案1】:问题在于 Worksheet.Evaluate
方法被用来绕过 UDF 不允许修改工作表结构的限制。
考虑这段代码
Option Explicit
Public Function dummyudf() As String
Debug.Print "Calling Evaluate method"
ActiveSheet.Evaluate "testsub()"
Debug.Print "Returning From Evaluate method"
dummyudf = "done"
End Function
Sub testsub()
Debug.Print "testsub running"
End Sub
Sub testmacro()
Dim s As String
Debug.Print "testmacro running"
s = dummyudf
End Sub
UDF dummyudf()
使用 Evaluate
方法将 invoke
Sub
称为 testsub()
。这些类似于 OP 的第 1 部分中的 mytest
和 test
以及第 2 部分中的 CellToRange
和 CTR
,但被精简到最低限度。
testsub()
也可以直接作为宏调用。第二个宏 testmacro
调用 dummyudf
作为 VBA 中的函数。
从即时窗口获得以下输出:
可以看出
当作为宏调用时:testsub()
的行为符合预期
当 dummyudf()
作为工作表上的 UDF 调用时(例如,通过将公式 =dummyudf()
添加到单元格 A1
中,Evaluate
方法似乎调用 testsub()
两次
dummyudf()
作为 VBA 中的函数调用时,通过将 testmacro()
作为宏运行,Evaluate
方法似乎调用了两次 testsub()
。
here 的文档建议 Worksheet.Evaluate
方法的 Name 参数应该是对象的名称,因此可以提供 @ 的名称有点令人惊讶987654349@。它似乎也调用了任何这样的Sub
两次,这更令人惊讶,但确实强调了 YowE3K 的回答中给出的关于不在 UDF 中使用此 hack 的建议。我会更进一步:不要将Worksheet.Evaluate
与任何Sub
一起使用。
【讨论】:
很抱歉我不得不这样做,好奇心害死猫。【参考方案2】:1) 它在公式被触发时计算一次,当单元格 A3 被函数更新时再次计算(因为它是公式所依赖的单元格之一)。
2a) 你调用了错误的子程序(test
而不是CTR
)
2b) 您需要使用类似
的方式调用第二个函数=CellToRange(A1;A3;""",""";""";""")
或者将代码中调用 CTR 的行更改为
dest.Parent.Evaluate "CTR(" & src.Address(False, False) & ", " & dest.Address(False, False) & ", """ & DelimL & """, """ & DelimC & """)"
3) 我强烈建议您不要使用这种 hack 来获取 UDF 来更新包含该函数的单元格以外的单元格。
【讨论】:
@ YowE3k 1) 函数不应该被调用两次来评估两次吗?当我在函数开始时切换断点并在工作表上计算时,我看到它只运行了 1 次。对不起,如果我理解不正确。 2a) 行动! ^^' 2b) 做到了 3) 我只是为了提高才做到的 @ YowE3K 2a) 嗯,它没有成功 2b) 更改了行,现在它将 srccell
复制到 dest cell
(hmmm) 另外,当我计算 CellToRange 时,它的评估结果没有像 my test
@AntiDrondert 1) 是的,看来我弄错了。单步执行代码显示执行一次评估,并且在函数完成之前根本不会更新工作表。我尝试偏移它写入的位置,这样它就不会写入引用的单元格,它仍然会复制东西。 2b)这两种方法对我来说都可以(除了重复)
2b) 忘记了另一个双引号(太痛苦了,几乎和 Lisp 中的括号一样糟糕)现在它工作正常......除了重复。也许是有原因的? Evaluate 可以充当“呼叫”Sub 两次吗?我不熟悉这种方法。以上是关于Excel UDF 将评估的 SUB 的值加倍的主要内容,如果未能解决你的问题,请参考以下文章