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 而不是Rangerng,工作正常。但是,如果我尝试在 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窗口,是怎么回事?

如何获取用户定义函数的地址?

在所有工作表中自动打开Excel文件到单元格A1(使用VBA)

Range的值等于先前范围的值和地址VBA

包含文件中的 ColdFusion 用户定义函数不可用

将数字从文本文件读取到 C 中用户定义函数中的数组