Excel vba 运行时错误 424 分配范围变量时需要对象



我有这段代码,它由一个函数和使用该函数的子例程组成。函数将列作为参数,并更改该列单元格的每个内容。

Function testFunc(aCol As Range)
Dim i As Integer
i = 0
For Each cell In aCol
cell.Value = i
i = i + 1
Next
End Function

Sub testSub()
Dim aRange As Range
Set aRange = ThisWorkbook.Sheets("Sheet1").Range("A1:A16")
testFunc (ThisWorkbook.Sheets("Sheet2").Range("A1:A16"))   '<--- this works fine
testFunc (aRange)    '<---- error
End Sub

Runtime error '424' Object required"| arises on linetestFunc (aRange)'。我不确定出了什么问题,我通过"Set"分配变量,它应该可以正常工作。

此外,我尝试将范围直接传递给函数,而无需先将其存储在变量中,它运行良好。为什么

表达式周围的括号会导致表达式被计算为值,并ByVal传递给调用的过程。

testFunc (ThisWorkbook.Sheets("Sheet2").Range("A1:A16"))   '<--- this works fine

不,它不能正常工作,testFunc签名中的aCol As Range不是 - 原因与其下方的行完全相同。Range对象具有指向其.Value的默认成员。执行此操作时:

DoSomething (SomeRange)

真正发生的事情是这样的:

DoSomething (SomeRange.Value)

括号告诉 VBA 在将该表达式的值 (ByVal)的结果传递给要调用的过程之前,将该表达式的值 () 计算表达式作为值,而不考虑过程签名中的隐式ByRef修饰符。

问题是,给定一个 16 个单元格RangeRange.Value是一个 2D 数组 - 并且给定一个单单元格RangeRange.Value是一个包含该单元格值的Variant并且不可能被强制到Range对象实例中,因此您在此处的代码不可能"正常工作":这是一个对象需要错误, 因为该过程需要Range,而您正在为其提供包含 2D 数组的Variant


为什么你的"工作正常"代码实际上不起作用

testFunc (ThisWorkbook.Sheets("Sheet2").Range("A1:A16"))可能工作的唯一方法是,如果testFunc有这个签名(我怀疑在你最终问这个问题之前,它曾经有过

):
Function testFunc(aCol)

或显式等效项:

Public Function testFunc(ByRef aCol As Variant) As Variant

而且由于 2D 数组可以使用For Each循环进行迭代(尽管效率非常低),并且整个A1:A16范围可能包含可以强制转换为有效Integer值的值,因此确实"工作正常"......除了您更新的是数据的内存副本,而不是单元格本身- 所以它不是"工作正常",更像是"不会崩溃"。

这是一个MCVE:

Private Function DoSomething(ByRef foo As Range)
End Function
Public Sub Test()
DoSomething (Range("A1").Value)
End Sub

调用Test会引发运行时错误 424"需要对象"。

Public Function DoSomething(ByRef foo As Range)
End Function
Public Sub Test()
DoSomething (Range("A1:A100").Value)
End Sub

调用Test会引发相同的运行时错误 424"需要对象"。

现在删除As Range声明:

Public Function DoSomething(ByRef foo As Variant)
Debug.Print TypeName(foo)
End Function
Public Sub Test()
DoSomething (Range("A1:A100").Value)
End Sub

调用Test会将Variant()打印到即时窗格(Ctrl+G)。现在使范围仅包含一个单元格:

Public Function DoSomething(ByRef foo As Variant)
Debug.Print TypeName(foo)
End Function
Public Sub Test()
DoSomething (Range("A1").Value)
End Sub

调用Test打印单元格 A1 的值所包含的任何数据类型。如果是文本,则打印String.如果是数字,则打印Double。如果是日期,则打印Date.如果是单元格错误(例如#NA#VALUE!等),它打印Error

请注意,无论您是否显式指定.Value,上述所有结果都是相同的,因为省略.Value归结为隐式指定它。

另请注意,传递数组ByVal是非法的。这里传递给函数的是指向数组开头的指针的副本 -Variant数组不存储数组本身,只是指向其实际位置的指针:这就是为什么以及如何合法地按值传递变体数组。


为什么变量也爆炸了

出于同样的原因,第 2 行抛出相同的错误:

testFunc (aRange)    '<---- error

你隐式这样做:

testFunc aRange.Value

因为这就是括号的作用:它们告诉 VBA 将表达式评估为值,并按值传递它。

因此,您将 2D 变体数组传递给期望Range的过程。

这是不同的,也是正确的:

testFunc aRange

请注意缺少括号:现在您将Range对象引用传递给接受Range对象引用的过程,ByRef(尽管该修饰符是隐式的,并且语义上更正确的方法是传递对象引用ByVal)。


那么,我如何判断参数何时被评估为值呢?

调用Function过程时,通常对其返回值感兴趣:

result = MsgBox("Confirm?", vbYesNo)

VBA 会自动将左括号紧跟在函数标识符之后 - 没有空格。

调用Function过程而不捕获其返回值时,必须省略括号:

MsgBox "Confirm?", vbYesNo

请注意,这将是一个编译错误:

MsgBox ("Confirm?", vbYesNo)

因为表达式("Confirm?", vbYesNo)不能作为值计算并传递给过程的第一个参数。

不过这很容易 -MsgBox有多个参数。当只有一个参数时,人们会感到困惑 - 因为这看起来是合法的:

testFunc (aRange)

注意空格:它不应该在那里。这是 VBE 告诉你"这些括号不是函数调用的一部分,它们是你要传递给该函数的参数的一部分,如果这些括号中的任何内容在运行时无法评估为值,预计会有麻烦"。


如果testFunc过程未返回值,则它首先不应该是Function- 它应该是一个Sub过程。函数接受输入并返回一个值,Subs做一些事情,这通常会带来副作用,例如修改实际的单元格值。

你的代码很像这个。

Sub testFunc(aCol As Range)
Dim i As Integer
i = 0
For Each cell In aCol
cell.Value = i
i = i + 1
Next
End Sub

Sub testSub()
Dim aRange As Range
Set aRange = ThisWorkbook.Sheets("Sheet1").Range("A1:A16")
testFunc aRange    '<---- error
End Sub

最新更新