在 VBA 函数调用中使用括号的规则是啥?
Posted
技术标签:
【中文标题】在 VBA 函数调用中使用括号的规则是啥?【英文标题】:What are the rules governing usage of parenthesis in VBA function calls?在 VBA 函数调用中使用括号的规则是什么? 【发布时间】:2011-03-24 01:34:48 【问题描述】:由于我在传递给我定义的 Sub 的参数周围使用括号而导致 VBA (Access 2003) 中出现“编译器错误”,这令人恼火 30 分钟。
我一直在寻找一篇关于何时需要/适当/不适当/禁止括号的体面文章/教程/说明,但找不到任何明确的指导方针。
【问题讨论】:
这是我最喜欢的关于这个主题的帖子:dailydoseofexcel.com/archives/2012/05/01/… 要清楚,你问的是括号 () 而不是括号 []。 【参考方案1】:VB(A)中的括号规则有完美的逻辑,它是这样的。
如果使用参数调用过程(函数或子程序),并且调用与其他语句或关键字位于同一行,则参数必须用括号括起来。这是为了将属于过程调用的参数与该行的其余部分区分开来。所以:
1: If CheckConditions(A, B, C) = DONT_PROCEED Then Exit Sub
是一个有效的行;对 CheckConditions 的调用需要括号来指示该行的其他哪些位是它的参数。相反,这会产生语法错误:
2: If CheckConditions A, B, C = DONT_PROCEED Then Exit Sub
因为无法解析。
将过程调用作为该行中唯一的语句,不需要括号,因为很明显参数属于过程调用:
3: SaveNewValues Value1, Value2, Value3
虽然这会导致语法错误(出于下面讨论的合理原因):
4: SaveNewValues(Value1, Value2, Value3)
为了避免混淆括号或没有括号(实际上,要完全避免括号规则),对此类调用使用 Call 关键字总是一个好主意;这确保过程调用不是该行中唯一的语句,因此需要括号:
5: Call SaveNewValues(Value1, Value2, Value3)
因此,如果您习惯于在自包含过程调用之前使用 Call 关键字,则可以忘记括号规则,因为您可以始终将参数括在括号中。
括号在 VB(A)(和许多其他语言)中扮演的额外角色使问题变得混乱:它们还指示表达式的计算优先级。如果您在任何其他上下文中使用括号而不是包含过程调用参数,VB(A) 将尝试将括号中的表达式计算为结果简单值。
因此,在示例 4 中,括号对于包含参数是非法的,VB(A) 将改为尝试计算括号中的表达式。由于 (Value1, Value 2, Value3) 不是可以计算的表达式,因此会出现语法错误。
这也解释了为什么如果参数包含在括号中,则使用传递 ByRef 的变量调用就像调用 ByVal 一样。在上面的例子中,函数 p 使用 ByRef 参数 a 被调用,这两个对 p 的调用有很大的不同:
6: p a
和
7: p(a)
如上所述,6 是正确的语法:调用是单独的,因此不应使用括号括住参数。
在 7 中,无论如何,参数都包含在括号中,提示 VB(A) 将包含的表达式计算为一个简单值。这当然是传递 ByVal 的定义。括号确保传递 a 的值而不是指向 a 的指针,并且 a 保持不变。
这也解释了为什么括号规则似乎并不总是占据主导地位。最明显的例子是 MsgBox 调用:
8: MsgBox "Hello World!"
还有
9: MsgBox ("Hello World!")
都是正确的,即使括号规则规定 9 应该是错误的。当然是这样,但所发生的只是 VB(A) 计算括号中的表达式。并且字符串文字计算为完全相同的字符串文字,因此实际调用是 8。换句话说:使用常量或字符串文字参数调用单参数过程具有相同的结果,带或不带括号。 (这就是为什么我的 MsgBox 调用前面都带有 Call 关键字的原因。)
最后,这解释了在传递 Object 参数时奇怪的类型不匹配错误和奇怪的行为。假设您的应用程序有一个 HighlightContent 过程,该过程将 TextBox 作为参数(而且,您永远猜不到,它会突出显示它的内容)。你调用它来选择文本框中的所有文本。您可以通过三种语法正确的方式调用此过程:
10: HighlightContent txtName
11: HighlightContent (txtName)
12: Call HighlightContent(txtName)
假设您的用户在文本框中输入了“John”并且您的应用程序调用了 HighlightContent。会发生什么,哪个调用会起作用?
10 和 12 是正确的;名称 John 将在文本框中突出显示。但是 11 在语法上是正确的,但会导致编译或运行时错误。为什么?因为括号不合适。这将提示 VB(A) 尝试评估括号中的表达式。一个对象的评估结果通常是其默认属性的值; .Text,在这种情况下。所以像11这样调用过程不会将TextBox对象传递给过程,而是一个字符串值“John”。导致类型不匹配。
【讨论】:
+1 是一个很好的答案,但我仍然不同意括号规则是“完全合乎逻辑的”......我无法想象一种更笨拙的方式来处理像括号这样简单的事情! 如果有“点”怎么办? (请随时更正我的术语)myCollection.add obj
和myCollection.item(obj)
这不是正确的方法吗?但是括号规则不一样,不知道为什么。
对我困惑了一段时间的事情的彻底回答。看起来还是有点傻。其他语言在解析带括号且没有“call”关键字的函数调用时没有任何问题。但是现在我知道了规则,我不会浪费时间试图弄清楚 WTFITMWTSL!,感谢您的帮助。 B^J
Call Debug.Print("Hello world")
仍然会引发错误。这背后的逻辑在哪里?
@Microsoft,帮自己一个忙,将您的vba/language/concepts/getting-started/using-parentheses-in-code 重定向到这里。【参考方案2】:
来自Here:
使用 VBScript 调用语句调用子程序 当您希望调用子程序时,使用 Call 语句是可选的。与 Sub 一起使用时 Call 语句的目的是允许您将参数列表括在括号中。但是,如果子例程不传递任何参数,那么在使用 Call 语句调用 Sub 时仍不应使用括号。
Call MySubroutine
如果子例程有参数,则在使用 Call 语句时必须使用括号。如果有多个参数,则必须用逗号分隔参数。
Call MySubroutine(intUsageFee, intTimeInHours, "DevGuru")
调用函数 调用函数有两种可能的方式。您可以仅按名称直接调用该函数,也可以使用 VBScript Call 语句调用它。
按名称调用函数 当直接按名称调用函数并且没有对返回值赋值时,以下所有语法都是合法的:
MyFunction
MyFunction()
MyFunction intUsageFee, intTimeInHours, "DevGuru"
如果需要返回值,可以将函数分配给变量。请注意,如果有一个或多个参数,则必须使用括号。
returnval = MyFunction
returnval = MyFunction()
returnval = MyFunction(intUsageFee, intTimeInHours, "DevGuru")
【讨论】:
谢谢 - 看起来我的问题是因为我的函数没有返回值,但我仍然在我的参数列表周围使用括号。这似乎是一个相当奇怪的语法决定......【参考方案3】:我刚刚发现调用带有 / 不带括号的函数的一些奇怪行为。 Google 把我带到了这里。
sub test()
dim a as double
a = 1#
p(a) 'this won't change a's value
Debug.Print a '1
p a ' this is expected behavior
Debug.Print a '2
Call p(a) 'this is also valid
Debug.Print a '3
end sub
Function p(a as Double) 'default is byref
a = a + 1
end function
我的结论是,当调用只有一个参数的函数时,您必须使用 Call 或省略括号,否则参数不会通过引用传递(它仍然会被调用,正如我已经检查过的那样)。
【讨论】:
括号确实强制传递参数ByVal
。【参考方案4】:
我只花了 10 分钟找出一个“类型不兼容”的异常,同时调用一个接受 1 个参数的 Sub
CallMe(argument)
事实证明,这是无效的,谷歌搜索最终将我带到这里
Call CallMe(argument)
或
CallMe argument
成功了。所以在调用 sub 时不能使用方括号,而调用语句只需要 1 个参数。
【讨论】:
+_1 表示子名称【参考方案5】:当你使用
Call MySub
你应该在参数周围使用括号,但是如果你省略Call,你就不需要括号了。
【讨论】:
【参考方案6】:1 - 默认情况下,调用过程或函数时不要使用括号:
MsgBox "Hello World"
2 - 如果你正在调用一个函数,并且对它的结果感兴趣,那么你必须用括号括起来它的参数:
Dim s As String
Dim l As Long
s = "Hello World"
l = Len(s)
3 - 如果你想在过程中使用 call 关键字,那么你必须用括号括住参数(例如,当你想将结果分配给变量或在表达式中使用函数时):
Call MsgBox("Hello World")
4 - 如果您想强制通过 ByVal 传递 ByRef 参数(默认值),则将 ByRef 参数用括号括起来:
Sub Test
Dim text As String
text = "Hello World"
ChangeArgument((text))
MsgBox text
End Sub
Sub ChangeArgument(ByRef s As String)
s = "Changed"
End Sub
这会显示“Hello World”
【讨论】:
【参考方案7】:好吧,这是很久以前提出的,但我刚刚遇到了这个问题,我发现这个问题我觉得还没有完全回答。希望我能对这个问题有所了解,以便为新手服务。
正如我所看到的,以前的答案主要关注这样一个事实,即无论何时使用 "Call" 语句,都必须将参数括在括号内。虽然这是真的1,但它绝对不是触发这种“奇怪”语法错误的主要来源。
克里斯托弗已经简要地指出了关键点。我只是参考文档并进一步解释一下。
参考文档2
所以重点是括号决定了你是否对你调用的函数/子/方法/语句的返回值感兴趣,即是否是否必须返回以将其存储在变量中。
说了这么多可能会遇到几个问题
用括号调用不返回值3的过程。Sub no_value_return(x as Integer)
Dim dummy as Integer
dummy = x
End Sub
'Error
no_value_return(1)
'No error
no_value_return 1
用括号调用返回值但不将其分配给变量的过程
Function value_return(ByVal x as Integer)
Dim value_return as Integer
value_return = x*2
End Function
'Error:
value_return(1)
'No error
Dim result as Integer
result = value_return(1)
一些额外的例子
'Error - No value returned since no parenthesis were specified
Dim result as Integer
result = value_return 1
'No error - Special case
Dim result as Variant
result = value_return 1
'The reason for this is that variant is the only data type that accepts
'the special value "Empty"
'No error - You can perfectly ignore the returned value even if it exists
value_return 1
1https://docs.microsoft.com/en-us/office/vba/language/concepts/getting-started/calling-sub-and-function-procedures
2https://docs.microsoft.com/en-us/office/vba/language/concepts/getting-started/using-parentheses-in-code
3 请注意,这不适用于函数过程或内置函数,因为它们必须始终返回一个值
【讨论】:
return dummy
- 这不是 VBA。 Sub
从不返回值,只返回函数。 -- result = value_return 1
- 无论结果类型如何,这永远不会奏效。 但是编辑器并不神奇,有时它会丢失跟踪并且不会应用补丁这甚至意味着什么。
哎呀,你说得对,我打错了 return_value 过程的声明,抱歉。对于补丁,我的意思是在括号前加一个空格,也许措辞有点混乱,我应该改变它吗?。
整个概念是错误的。编辑器没有“修复”任何东西,括号改变了参数的评估。 Floris Kleijne 的回答很好地解释了这一切。 - 我评论中的其他问题仍然适用。请尝试实际运行您编写的所有示例代码。
抱歉,刚刚注意到您提到返回语法不正确,因此也进行了更改。
我实际上不知道括号的前置是否是记录的行为,但这是我在编程时注意到的。每当我调用一个子程序(没有返回的程序)时,编辑器都会自动添加一个空格。我假设这样做是为了保留用户符号但同时避免语法错误。【参考方案8】:
我使用另一种逻辑来区分何时使用括号。如果您的函数不返回值(C 语言中的 void 类型),则不需要括号。对于 subs 来说总是如此,因为返回值是 sub 和 function 之间的主要区别。否则你必须使用括号。
【讨论】:
以上是关于在 VBA 函数调用中使用括号的规则是啥?的主要内容,如果未能解决你的问题,请参考以下文章
lua 函数调用的时候使用小括号和使用大括号有啥区别,如何定义?