简体   繁体   English

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

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

I use the Call keyword when calling subs in VB/VBA.我在 VB/VBA 中调用 subs 时使用Call关键字。 I know it's optional, but is it better to use it or leave it off?我知道它是可选的,但是使用它还是放弃它更好? I've always thought it was more explicit, but maybe it's just noise.我一直认为它更明确,但也许这只是噪音。

Also, I read this on another forum: Using the Call keyword is faster because it knows that it is not going to return any values, so it doesn't need to set up any stackspace to make room for the return value.另外,我在另一个论坛上读到:使用Call关键字更快,因为它知道它不会返回任何值,因此它不需要设置任何堆栈空间来为返回值腾出空间。

Ah ha.啊哈。 I have long wondered about this and even reading a two inch thick book on VBA basically says don't use it unless you want to use the Find feature of the VBE to easily find calls in large projects.我一直想知道这个问题,甚至阅读一本两英寸厚的关于 VBA 的书基本上都说不要使用它,除非您想使用 VBE 的查找功能轻松查找大型项目中的调用。

But I just found another use.但我刚刚发现了另一个用途。

We know that it's possible to concatenate lines of code with the colon character, for example:我们知道可以用冒号字符连接代码行,例如:

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

But if you do this with procedure calls at the beginning of a line, the VBE assumes that you're referring to a label and removes any indents, aligning the line to the left margin (even though the procedure is called as intended):但是,如果您在一行的开头对过程调用执行此操作,则 VBE 假定您指的是一个标签并删除任何缩进,将行与左边距对齐(即使该过程按预期调用):

Function Test()
Function1 : Function2
End Function

Using the Call statement allows concatenation of procedure calls while maintaining your code indents:使用Call语句允许在保持代码缩进的同时串联过程调用:

Function Test()
    Call Function1 : Call Function2
End Function

If you don't use the Call statement in the above example, the VBE will assume that "Function1" is an label and left align it in the code window, even though it won't cause an error.如果不使用上例中的Call语句,VBE 将假定“Function1”是一个标签,并将其在代码窗口中左对齐,即使它不会导致错误。

For VB6, if there is any chance it will be converted to VB.NET, using Call means the syntax doesn't change.对于 VB6,如果有任何机会将其转换为 VB.NET,使用Call意味着语法不会改变。 (Parentheses are required in VB.NET for method calls.) (I don't personally think this is worth the bother -- any .NET converter will at least be able to put in parentheses when required. I'm just listing it as a reason.) (在 VB.NET 中需要括号用于方法调用。)(我个人认为这不值得麻烦——任何 .NET 转换器至少可以在需要时放入括号。我只是将它列为一个原因。)

Otherwise it is just syntactic sugar.否则它只是语法糖。

Note the Call keyword is likely not to be faster when calling some other method/function because a function returns its value anyway, and VB didn't need to create a local variable to receive it, even when Call is not used.请注意, Call其他方法/函数时, Call关键字可能不会更快,因为函数无论如何都会返回其值,并且即使不使用Call ,VB 也不需要创建局部变量来接收它。

I always use Call in VBA.我总是在 VBA 中使用Call To me, it just looks cleaner.对我来说,它看起来更干净。 But, I agree, it's just syntactic sugar, which puts it squarely the realm of personal preference.但是,我同意,这只是语法糖,这完全符合个人喜好。 I've come across probably a dozen full time VBA guys in the past few years, and not one of them used Call .在过去的几年里,我可能遇到过十几个全职 VBA 人员,但没有一个人使用过Call This had the added advantage that I always knew which code was mine.这有一个额外的好处,我总是知道哪个代码是我的。 :p :p

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

I use Call for all VBA development of common library functions that I possibly will use in VB.NET.我将Call用于我可能会在 VB.NET 中使用的公共库函数的所有 VBA 开发。 This allows me to move code using copy and paste between all the flavors of VB.这允许我使用复制和粘贴在 VB 的所有风格之间移动代码。 I do this to avoid the syntax errors that the code editor creates when it "formats" or "pretty prints" the pasted code.我这样做是为了避免代码编辑器在“格式化”或“漂亮打印”粘贴的代码时产生的语法错误。 The only edits are usually Set statement inclusion/exclusion.唯一的编辑通常是Set语句包含/排除。

If you have no plans to move your VB/VBA code to VB.NET, then there is no need to use the Call statement.如果您不打算将 VB/VBA 代码移动到 VB.NET,则无需使用Call语句。

No one covered this important distinction: in some (common) situations, Call prevents parentheses around function (and sub) arguments from causing the arguments to be strictly interpreted as ByVal .没有人提到这一重要区别:在某些(常见)情况下,Call 可防止函数(和子)参数周围的括号导致参数被严格解释为ByVal

The big takeaway for you is that if you DO use parentheses around arguments to a routine, perhaps by rote or habit, even though they are not required, then you SHOULD USE Call to ensure that the routine's implicit or explicit ByRef is not disregarded in favor of ByVal ;对你来说最大的收获是,如果你确实在例程的参数周围使用括号,可能是死记硬背或习惯,即使它们不是必需的,那么你应该使用Call来确保例程的隐式或显式ByRef不被忽视ByVal or, instead, you should use an "equal sign" assignment of the return value to prevent the disregard (in which case you would not use Call ).或者,您应该使用返回值的“等号”分配来防止忽略(在这种情况下您不会使用Call )。

Again, that is to protect you from unfavorably getting ByVal from a routine.同样,这是为了保护您免于从例程中不利地获得ByVal Conversely, of course, if you WANT ByVal interpretation regardless of the routine's declaration, then LEAVE OFF the Call (and use parentheses).相反,当然,如果您想要ByVal解释而不管例程的声明,那么请离开Call (并使用括号)。

Rationale: summarizing "ByRef and ByVal Parameters"基本原理:总结“ByRef 和 ByVal 参数”

If如果
1. there is an assignment of a function call retval, eg 1. 有一个函数调用 retval 的赋值,例如

iSum = myfunc(myArg)  

or或者
2. " Call " is used, eg 2. 使用“ Call ”,例如

call myFunc(myArg)  

or或者

call mySub(myArg)

then the parentheses strictly delineate the calling argument list;然后括号严格地描述了调用参数列表; the routine declaration determines ByVal or ByRef.例程声明确定 ByVal 或 ByRef。 OTHERWISE the parentheses force ByVal to be used by the routine - even though ByVal was not specified in the routine.否则,括号强制例程使用 ByVal - 即使例程中未指定 ByVal。 Thus,因此,

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

Also note that Call syntactically mandates use of parentheses.另请注意,Call 在语法上要求使用括号。 You can go你可以走了

mySub myArg  

but you can't go但你不能去

call mySub myArg  

but you CAN go但你可以去

call mySub(myArg)  

(and parentheses are syntactically required for assignment of Function return value) (在语法上需要括号来分配函数返回值)

NOTE however that ByVal on the routine declaration overrides all of this.但是请注意,例程声明中的ByVal会覆盖所有这些。 And FYI, ByRef is always implied in the declaration if you are silent;仅供参考,如果您保持沉默, ByRef总是隐含在声明中; thus TMK ByRef has no apparent value other than documentary.因此,TMK ByRef除了纪录片之外没有明显的价值。

Repeating from above: The big takeaway for you is that if you DO use parentheses around arguments to a routine, perhaps by rote or habit, even though they are not required, then you SHOULD USE Call to ensure that the routine's implicit or explicit ByRef is not disregarded in favor of ByVal ;从上面重复:对你来说最大的收获是,如果你确实在例程的参数周围使用括号,可能是死记硬背或习惯,即使它们不是必需的,那么你应该使用Call来确保例程的隐式或显式ByRef是没有被忽视而有利于ByVal or, instead, you should use an "equal sign" assignment of the return value to prevent the disregard (in which case you would not use Call ).或者,您应该使用返回值的“等号”分配来防止忽略(在这种情况下您不会使用Call )。

Again, that is to protect you from unfavorably getting ByVal from a routine.同样,这是为了保护您免于从例程中不利地获得ByVal Conversely, of course, if you WANT ByVal interpretation regardless of the routine's declaration, then LEAVE OFF the Call (and use parentheses).相反,当然,如果您想要ByVal解释而不管例程的声明,那么请离开Call (并使用括号)。

If you read the MSDN Support page for the Call Statement , for the specific case o VBA, at least, it does say that Call is optional, but what is very relevant about it and nobody seems to notice is this quoted line:如果您阅读了Call StatementMSDN 支持页面,至少对于 VBA 的特定情况,它确实说Call是可选的,但是与它非常相关并且似乎没有人注意到以下引用的行:

" If you use either Call syntax to call any intrinsic or user-defined function, the function's return value is discarded. " "如果您使用 Call 语法来调用任何内部函数或用户定义函数,则该函数的返回值将被丢弃。 "

This is why Call is far from useless.这就是Call远非无用的原因。 Say you're writing Sub SupportTasks that does a lot of very relevant stuff for you Main Subs (for example, it imports data from a file to be used by different procedures).假设您正在编写 Sub SupportTasks ,它为您的Main Subs 做了很多非常相关的事情(例如,它从文件中导入数据以供不同的过程使用)。 Now, notice that since SupportTasks is reading external data, there's always a fat chance this data will not come standard and the sub will not be able to fulfill its role.现在,请注意,由于SupportTasks正在读取外部数据,因此这些数据很可能不符合标准并且 sub 将无法履行其职责。 What do you do?你做什么工作?

You could, for example, use boolean functions that return False if something goes wrong.例如,您可以使用在出现问题时返回False 的布尔函数。 Instead of calling a sub, call a function SupportTasks inside and If statement that will exit the Main sub if there's an anomaly:不是调用子函数,而是在内部调用函数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

If you're wondering what the heck this has to do with Call , consider the following: in another sub, I call SupportTasks , but I do not need its returned boolean value (for instance, I'm certain an error won't occur).如果您想知道这与Call有什么关系,请考虑以下事项:在另一个子程序中,我调用SupportTasks ,但我不需要它返回的布尔值(例如,我确定不会发生错误)。 Well, if I don't put it in an If statement or assign the function to a useless variable, VBA will not compile and return me an error ( procedure call invalid blah blah blah must assign value to something blah blah blah ).好吧,如果我不把它放在If语句中或将函数分配给一个无用的变量,VBA 将不会编译并返回一个错误(过程调用无效等等等等必须为某些东西赋值等等等等)。 That's where Call comes in to save the day!这就是Call 的用武之地!

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

If you still think it's useless, think of it as a resource to stay organized.如果您仍然认为它没有用,请将其视为保持井井有条的资源。 Writing separate procedures for routines shared by many procedures makes your code shorter and more comprehensible , specially when you're writing really large applications.为许多过程共享的例程编写单独的过程可以使您的代码更短且更易于理解,尤其是在您编写非常大的应用程序时。 ERPs built out of Excel-Access integrations, for example, can be easier to operate, repair and customize if your IT dept slow to deliver/implement the real system...例如,如果您的 IT 部门交付/实施真实系统的速度缓慢,则基于 Excel-Access 集成构建的 ERP 可以更轻松地操作、修复和定制...

To conclude, some internet wisdom:总而言之,一些互联网智慧:

Always write your code as if the person who will review it is a murderous psychopath who knows where you live.始终编​​写您的代码,就好像审查它的人是一个知道您住在哪里的凶残的精神病患者一样。

Amen.阿门。

I'm 7 years late to the party, but I just happened to come across the Call keyword a few minutes ago while reading something on MSDN.我参加聚会晚了 7 年,但几分钟前我在 MSDN 上阅读某些内容时碰巧遇到了Call关键字。 In particular, it was used to do something I thought was impossible in VB.NET (as opposed to C#) -- which is related to @FCastro's answer.特别是,它被用来做一些我认为在 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

In the odd case you don't need the actual object instance but require one of its methods, you can use Call to save a line.在奇怪的情况下,您不需要实际的对象实例但需要它的方法之一,您可以使用Call来保存一行。 Note that this is unnecessary when it's the right-hand side of an operation:请注意,当它是操作的右侧时,这是不必要的:

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

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

The only case I found "call" is useful is quite an accident, about some special operators.我发现“调用”有用的唯一情况是非常偶然的,关于一些特殊的操作符。

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

I got a syntax error for the second line, just like what you'll get with a "New" operator.我在第二行遇到语法错误,就像使用“New”运算符会得到的结果一样。 I really don't want a new variable, which is too inelegant for me.我真的不想要一个新变量,这对我来说太不雅了。 So I tried:所以我试过:

DirectCast(Await c, StartupTask).Disable()

This is syntactically correct.这在语法上是正确的。 But then the IDE hinted me that the "DirectCast" is unnecessary and gave a simplification.但随后 IDE 提示我“DirectCast”是不必要的,并进行了简化。 Yes, that is:对,是:

Call (Await c).Disable()

That's why I love VS2017 Preview.这就是我喜欢 VS2017 预览版的原因。 😄 😄

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

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