Personal.xlsb 文件中的用户定义函数挂起 Excel。有没有办法避免这种情况并优化执行?
Posted
技术标签:
【中文标题】Personal.xlsb 文件中的用户定义函数挂起 Excel。有没有办法避免这种情况并优化执行?【英文标题】:User Defined Function in Personal.xlsb file is hanging up Excel. Is there a way to avoid this, and optimize execution? 【发布时间】:2020-05-20 23:34:56 【问题描述】:我在 VBA 中创建了一个用户定义的函数,并将其保存在 Personal.xlsb 文件中,以使其可用于所有书籍。这个函数的目的是接收一个范围和一个数字。对于范围内的所有数字,第一步是将其舍入到作为参数接收的指定数字的下一个倍数。进行四舍五入后,下一步是计算众数。函数如下:
Function ROUNDEDMODEV(rng As Variant, multiple As Variant)
' Array with the values from the selected range.
Dim cellRange() As Variant
' Copy the specified range values to array
cellRange = rng
' Looping through the array.
Dim r As Long
Dim c As Long
For r = 1 To UBound(cellRange, 1) ' First array dimension is rows.
For c = 1 To UBound(cellRange, 2) ' Second array dimension is columns.
On Error Resume Next
cellRange(r, c) = WorksheetFunction.MRound(cellRange(r, c), multiple)
Next c
Next r
ROUNDEDMODEV = WorksheetFunction.mode(cellRange)
End Function
我这样称呼它的方式如下:
=PERSONAL.XLSB!ROUNDEDMODEV(IF($B$2:$B$20="some text", $C$2:$Z$20),0.5)
【问题讨论】:
【参考方案1】:考虑更改签名以使预期的数据类型更加明确:
Public Function ROUNDEDMODEV(ByVal rng As Range, ByVal multiple As Double) As Variant
Variant
可以很好地包装一个二维变体数组;考虑如下声明cellRange
:
Dim cellRange As Variant
rng
参数是 Range
(你的在运行时是 Variant/Range
),它是一个对象,并且对象不像值那样工作 - 这个赋值涉及一个名为 let强制:
cellRange = rng
Let-coerced 对象需要有一个 默认成员(如果没有,则会引发错误 438) - 对于 Excel.Range
类,默认成员是隐藏属性命名为[_Default]
,它本质上在参数化时调用Item
属性……否则调用Value
属性。所以我们可以更明确地说明正在发生的事情,就像这样:
cellRange = rng.Value
对此有一点说明:
For c = 1 To UBound(cellRange, 2) ' Second array dimension is columns. On Error Resume Next
因为永远不会恢复错误处理 (On Error GoTo 0
),所以只有第一次迭代才能看到 On Error
语句有任何效果。考虑在“危险”语句之后恢复错误处理:
On Error Resume Next
cellRange(r, c) = WorksheetFunction.MRound(cellRange(r, c), multiple)
On Error GoTo 0
如果出现错误,它将在评估赋值的右侧时;在这种情况下,cellRange(r, c)
将保持为Variant/Empty
。
如果需要,您可以使用CVErr
函数使Variant
-returning 函数产生工作表错误:
ROUNDEDMODEV = CVErr(xlErrValue) 'xlErrNA, etc.
如果您的函数不需要返回错误代码,请考虑明确声明其返回类型:
Public Function ROUNDEDMODEV(ByVal rng As Range, ByVal multiple As Double) As Double
在测试您的 UDF 时,从 VBE 的 立即 工具窗口 (Ctrl+G) 调用它,而不是从工作表单元格调用它:#VALUE!
错误可能意味着您的函数正在引发未处理的错误,并且从 VBA 代码(或 立即 窗格)调用它会给你更多的调试信息。
【讨论】:
我已经添加了您之前指出的更正,但是我对即时窗口使用不熟悉。由于我在 Personal.xlsb 文件中有这个 UDF,我应该如何调用它?我试过了:PERSONAL.XLSB!ROUNDEDMODEV(IF($B$2:$B$20="test", $C$2:$Z$20),0.05),但它给了我错误提示“预期:表达式”跨度> 在 VBE 中激活带有 UDF 的模块,然后 ctrl+g 和?roundedmodev([IF($B$2:$B$20="test", $C$2:$Z$20)],0.05)
; ?
是 Print
的简写
抱歉再次询问。在立即窗口中尝试,如果我尝试使用 ? roundedmodev([IF($M$2:$M10="test",$N$2:$Q10)],0.05)
,则会出现“需要对象”错误。我还在函数的开头和cellRange = rng.Value
行中添加了一些断点,但我仍然得到#VALUE!错误(似乎没有进入函数)。
在即时窗口中运行相同,但在函数定义中使用Variant
而不是Range
为rng
,工作正常。但是,如果我尝试在 Excel 工作表中运行它,它将挂断。【参考方案2】:
替换:
Function ROUNDEDMODEV(rng As Variant, multiple As Variant)
与:
Function ROUNDEDMODEV(rng As Range, multiple As Variant)
在我的系统上 (Win 10 / Excel 365) VBA 无法将范围 rng
解析为二维数组 cellRange
,除非我明确地将范围设为范围。
【讨论】:
这是我以前使用该功能的方式,如果我这样做,它会给我#VALUE!错误。 @Helbirah#VALUE!
可能意味着您的函数正在引发未处理的错误。尝试从调试/立即窗格中调用它 (Ctrl+G)以上是关于Personal.xlsb 文件中的用户定义函数挂起 Excel。有没有办法避免这种情况并优化执行?的主要内容,如果未能解决你的问题,请参考以下文章
Excel打开文件时,会同时打开一个空白Excel窗口,是怎么回事?