Excel vba运行时错误424分配Range变量时所需的对象

时间:2017-07-05 12:38:27

标签: excel vba excel-vba

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

=TRIM(LEFT(SUBSTITUTE(REPLACE(H1,1,FIND("?",H1),TEXT(,)),"&",REPT(" ",LEN(H1))),LEN(H1)))&CHAR(10)&TRIM(MID(SUBSTITUTE(REPLACE(H1,1,FIND("?",H1),TEXT(,)),"&",REPT(" ",LEN(H1))),LEN(H1),LEN(H1)))

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 testFunc(aRange)`。我不知道出了什么问题,我通过&#39; Set&#39;分配变量。并且应该可以正常工作。

另外,我尝试将范围直接传递给函数,而不是先将它存储在变量中,它可以完美地工作。的为什么

2 个答案:

答案 0 :(得分:2)

表达式周围的括号导致表达式被评估为值并将ByVal传递给调用的过程。

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

不,它不能正常工作,而不是aCol As Range签名中的testFunc - 原因与其下方的行完全相同。 Range对象的默认成员指向其.Value。当你这样做时:

DoSomething (SomeRange)

真正发生的是:

DoSomething (SomeRange.Value)

括号告诉VBA 将表达式评估为值 之前将结果按值传递ByVal)该表达式对于被调用的过程,无论过程签名中的隐式ByRef修饰符。

问题是,给定16个单元格RangeRange.Value是一个2D数组 - 并且给定单个单元格RangeRange.ValueVariant包含该单元格的值,并且可能被强制转换为Range对象实例,因此您拥有的代码是不可能的在这里&#34;工作正常&#34;:它是对象所需的错误,因为该过程期待Range并且您正在给它Variant包含2D数组的1}}。

为什么你的&#34;工作正常&#34;代码并没有真正起作用

testFunc (ThisWorkbook.Sheets("Sheet2").Range("A1:A16"))可能有效的唯一方法是testFunc是否有此签名(我怀疑它在您最终提出此问题之前的某个时刻确实存在):

Function testFunc(aCol)

或明确的等价物:

Public Function testFunc(ByRef aCol As Variant) As Variant

因为可以使用For Each循环(albeit very inefficiently)迭代2D数组 ,并且可能整个A1:A16范围包含可以被强制转换为有效Integer值的值,&#34;工作正常&#34;确实......除了你更新内存中的数据副本,而不是单元格本身 - 所以它不是很好&#34;工作正常&#34;更像是&#34;没有崩溃&#34;。

这是一个MCVE:

Private Function DoSomething(ByRef foo As Range)
End Function

Public Sub Test()
    DoSomething (Range("A1").Value)
End Sub

调用Test会引发运行时错误424&#34; Object Required&#34;。

Public Function DoSomething(ByRef foo As Range)
End Function

Public Sub Test()
    DoSomething (Range("A1:A100").Value)
End Sub

调用Test会抛出相同的运行时错误424&#34; Object Required&#34;。

现在删除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

调用TestVariant()打印到即时窗格(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数组不是存储数组本身,只是指向其实际位置的指针:这就是为什么&amp;如何按值合法传递变量数组。

为什么变量也在爆炸

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

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告诉你&#34;这些括号不是函数调用的一部分,它们是你要传递给该函数的参数的一部分,如果有的话在这些括号中,不能在运行时评估为值,期待麻烦&#34;

如果您的testFunc程序没有返回值,那么它首先不应该是Function - 它应该是Sub程序。函数接受输入并返回一个值,Subs 做某事,这通常会带来副作用,例如:实际细胞值被修改。

答案 1 :(得分:0)

你的代码无疑会喜欢这个。

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