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”。导致类型不匹配。