我有这个代码,它包含一个使用该函数的函数和子例程。函数将列作为参数并更改每个列单元格的内容。
=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;分配变量。并且应该可以正常工作。
另外,我尝试将范围直接传递给函数,而不是先将它存储在变量中,它可以完美地工作。的为什么
答案 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个单元格Range
,Range.Value
是一个2D数组 - 并且给定单个单元格Range
,Range.Value
是Variant
包含该单元格的值,并且可能被强制转换为Range
对象实例,因此您拥有的代码是不可能的在这里&#34;工作正常&#34;:它是对象所需的错误,因为该过程期待Range
并且您正在给它Variant
包含2D数组的1}}。
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
调用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
数组不是存储数组本身,只是指向其实际位置的指针:这就是为什么&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