繁体   English   中英

我应该在 VB/VBA 中使用 Call 关键字吗?

[英]Should I use Call keyword in VB/VBA?

我在 VB/VBA 中调用 subs 时使用Call关键字。 我知道它是可选的,但是使用它还是放弃它更好? 我一直认为它更明确,但也许这只是噪音。

另外,我在另一个论坛上读到:使用Call关键字更快,因为它知道它不会返回任何值,因此它不需要设置任何堆栈空间来为返回值腾出空间。

啊哈。 我一直想知道这个问题,甚至阅读一本两英寸厚的关于 VBA 的书基本上都说不要使用它,除非您想使用 VBE 的查找功能轻松查找大型项目中的调用。

但我刚刚发现了另一个用途。

我们知道可以用冒号字符连接代码行,例如:

Function Test(mode as Boolean) 
    if mode = True then x = x + 1 : Exit Sub
    y = y - 1
End Sub

但是,如果您在一行的开头对过程调用执行此操作,则 VBE 假定您指的是一个标签并删除任何缩进,将行与左边距对齐(即使该过程按预期调用):

Function Test()
Function1 : Function2
End Function

使用Call语句允许在保持代码缩进的同时串联过程调用:

Function Test()
    Call Function1 : Call Function2
End Function

如果不使用上例中的Call语句,VBE 将假定“Function1”是一个标签,并将其在代码窗口中左对齐,即使它不会导致错误。

对于 VB6,如果有任何机会将其转换为 VB.NET,使用Call意味着语法不会改变。 (在 VB.NET 中需要括号用于方法调用。)(我个人认为这不值得麻烦——任何 .NET 转换器至少可以在需要时放入括号。我只是将它列为一个原因。)

否则它只是语法糖。

请注意, Call其他方法/函数时, Call关键字可能不会更快,因为函数无论如何都会返回其值,并且即使不使用Call ,VB 也不需要创建局部变量来接收它。

我总是在 VBA 中使用Call 对我来说,它看起来更干净。 但是,我同意,这只是语法糖,这完全符合个人喜好。 在过去的几年里,我可能遇到过十几个全职 VBA 人员,但没有一个人使用过Call 这有一个额外的好处,我总是知道哪个代码是我的。 :p

不,它只会在每次调用时添加 7 个字符而没有任何好处。

我将Call用于我可能会在 VB.NET 中使用的公共库函数的所有 VBA 开发。 这允许我使用复制和粘贴在 VB 的所有风格之间移动代码。 我这样做是为了避免代码编辑器在“格式化”或“漂亮打印”粘贴的代码时产生的语法错误。 唯一的编辑通常是Set语句包含/排除。

如果您不打算将 VB/VBA 代码移动到 VB.NET,则无需使用Call语句。

没有人提到这一重要区别:在某些(常见)情况下,Call 可防止函数(和子)参数周围的括号导致参数被严格解释为ByVal

对你来说最大的收获是,如果你确实在例程的参数周围使用括号,可能是死记硬背或习惯,即使它们不是必需的,那么你应该使用Call来确保例程的隐式或显式ByRef不被忽视ByVal 或者,您应该使用返回值的“等号”分配来防止忽略(在这种情况下您不会使用Call )。

同样,这是为了保护您免于从例程中不利地获得ByVal 相反,当然,如果您想要ByVal解释而不管例程的声明,那么请离开Call (并使用括号)。

基本原理:总结“ByRef 和 ByVal 参数”

如果
1. 有一个函数调用 retval 的赋值,例如

iSum = myfunc(myArg)  

或者
2. 使用“ Call ”,例如

call myFunc(myArg)  

或者

call mySub(myArg)

然后括号严格地描述了调用参数列表; 例程声明确定 ByVal 或 ByRef。 否则,括号强制例程使用 ByVal - 即使例程中未指定 ByVal。 因此,

    mySub(myArg)       'uses ByVal regardless of the routine's declaration, whereas  
    Call mySub(myArg)  'uses ByRef, unless routine declares ByVal

另请注意,Call 在语法上要求使用括号。 你可以走了

mySub myArg  

但你不能去

call mySub myArg  

但你可以去

call mySub(myArg)  

(在语法上需要括号来分配函数返回值)

但是请注意,例程声明中的ByVal会覆盖所有这些。 仅供参考,如果您保持沉默, ByRef总是隐含在声明中; 因此,TMK ByRef除了纪录片之外没有明显的价值。

从上面重复:对你来说最大的收获是,如果你确实在例程的参数周围使用括号,可能是死记硬背或习惯,即使它们不是必需的,那么你应该使用Call来确保例程的隐式或显式ByRef是没有被忽视而有利于ByVal 或者,您应该使用返回值的“等号”分配来防止忽略(在这种情况下您不会使用Call )。

同样,这是为了保护您免于从例程中不利地获得ByVal 相反,当然,如果您想要ByVal解释而不管例程的声明,那么请离开Call (并使用括号)。

如果您阅读了Call StatementMSDN 支持页面,至少对于 VBA 的特定情况,它确实说Call是可选的,但是与它非常相关并且似乎没有人注意到以下引用的行:

"如果您使用 Call 语法来调用任何内部函数或用户定义函数,则该函数的返回值将被丢弃。 "

这就是Call远非无用的原因。 假设您正在编写 Sub SupportTasks ,它为您的Main Subs 做了很多非常相关的事情(例如,它从文件中导入数据以供不同的过程使用)。 现在,请注意,由于SupportTasks正在读取外部数据,因此这些数据很可能不符合标准并且 sub 将无法履行其职责。 你做什么工作?

例如,您可以使用在出现问题时返回False 的布尔函数。 不是调用子函数,而是在内部调用函数SupportTasksIf语句,如果出现异常,它将退出 Main 子函数:

If Not SupportTasks(SomeArgument) Then
    Application.ScreenUpdating = True
    Exit Sub
'Else continue the Main sub regularly without writing anything in here
End If

如果您想知道这与Call有什么关系,请考虑以下事项:在另一个子程序中,我调用SupportTasks ,但我不需要它返回的布尔值(例如,我确定不会发生错误)。 好吧,如果我不把它放在If语句中或将函数分配给一个无用的变量,VBA 将不会编译并返回一个错误(过程调用无效等等等等必须为某些东西赋值等等等等)。 这就是Call 的用武之地!

Call SupportTasks(SomeArgument) '<< "Call Function" call doesn't return an error

如果您仍然认为它没有用,请将其视为保持井井有条的资源。 为许多过程共享的例程编写单独的过程可以使您的代码更短且更易于理解,尤其是在您编写非常大的应用程序时。 例如,如果您的 IT 部门交付/实施真实系统的速度缓慢,则基于 Excel-Access 集成构建的 ERP 可以更轻松地操作、修复和定制...

总而言之,一些互联网智慧:

始终编​​写您的代码,就好像审查它的人是一个知道您住在哪里的凶残的精神病患者一样。

阿门。

我参加聚会晚了 7 年,但几分钟前我在 MSDN 上阅读某些内容时碰巧遇到了Call关键字。 特别是,它被用来做一些我认为在 VB.NET 中不可能的事情(而不是 C#)——这与@FCastro 的回答有关。

Class Test
    Public Sub DoSomething()
        Console.WriteLine("doing something")
    End Sub
End Class

Sub Main()
    Call (New Test()).DoSomething()
End Sub

在奇怪的情况下,您不需要实际的对象实例但需要它的方法之一,您可以使用Call来保存一行。 请注意,当它是操作的右侧时,这是不必要的:

Class Test
    Public Function GetSomething() As Integer
        Return 0
    End Function
End Class

Sub Main()
    Dim x As Integer = (New Test()).GetSomething()
End Sub

我发现“调用”有用的唯一情况是非常偶然的,关于一些特殊的操作符。

Dim c As IAsyncOperation(Of StartupTask) = StartupTask.GetAsync("Startup")
……
(Await c).Disable()

我在第二行遇到语法错误,就像使用“New”运算符会得到的结果一样。 我真的不想要一个新变量,这对我来说太不雅了。 所以我试过:

DirectCast(Await c, StartupTask).Disable()

这在语法上是正确的。 但随后 IDE 提示我“DirectCast”是不必要的,并进行了简化。 对,是:

Call (Await c).Disable()

这就是我喜欢 VS2017 预览版的原因。 😄

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM