在 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 objmyCollection.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 函数调用中使用括号的规则是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Scala方法调用中括号的规则是啥?

lua 函数调用的时候使用小括号和使用大括号有啥区别,如何定义?

函数名周围的括号是啥意思?

C语言规定,调用一个函数时,实参变量和形参变量之间的数据传递方式是啥

python怎么调用自定义函数

vba中split()()后面的括号是啥意思