Should I use Call keyword in VB/VBA?
Asked Answered
J

10

53

I use the Call keyword when calling subs in VB/VBA. 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.

Jocosity answered 4/4, 2010 at 4:48 Comment(6)
In some cases using the call keyword is pretty useful, like when you just want to use a class once. I use it today to generate a random salt Call New RNGCryptoServiceProvider().GetBytes(salt) without Call I would have had to a variable as an RNGCryptoServiceProvider firstIsabel
That is easily the best (if not the only) reason to use to use call that I have ever heard of.Escapism
@Escapism except RNGCryptoServiceProvider inherits RandomNumberGenerator, which implements IDisposable, so this "pretty useful" use of Call is actually allocating unmanaged resources, and leaves them dangling, since Dispose is never invoked. In some contexts, the consequences of this can be catastrophic. Bad, bad idea IMO.Almonte
@Isabel I meant to ping you on this one as well ^Almonte
@MathieuGuindon That's a very good point, thanks!Isabel
@Isabel it doesn't work in VBA ("Syntax error")Billon
F
36

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.

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):

Function Test()
Function1 : Function2
End Function

Using the Call statement allows concatenation of procedure calls while maintaining your code indents:

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.

Falzetta answered 11/3, 2014 at 12:7 Comment(1)
+1 for a legitimage case... although I'd argue that having multiple instructions / function calls on a single line is a bad practice.Almonte
C
12

For VB6, if there is any chance it will be converted to VB.NET, using Call means the syntax doesn't change. (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.)

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.

Compensable answered 4/4, 2010 at 5:55 Comment(0)
K
12

I always use Call in VBA. 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. This had the added advantage that I always knew which code was mine. :p

Kinakinabalu answered 6/4, 2010 at 1:6 Comment(0)
C
4

No, it'll just add 7 characters per call with no given benefit.

Conjugal answered 6/4, 2010 at 1:18 Comment(0)
I
4

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.

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; 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).

Again, that is to protect you from unfavorably getting ByVal from a routine. Conversely, of course, if you WANT ByVal interpretation regardless of the routine's declaration, then LEAVE OFF the Call (and use parentheses).

Rationale: summarizing "ByRef and ByVal Parameters"

If
1. there is an assignment of a function call retval, e. g.

iSum = myfunc(myArg)  

or
2. "Call" is used, e. g.

call myFunc(myArg)  

or

call mySub(myArg)

then the parentheses strictly delineate the calling argument list; the routine declaration determines ByVal or ByRef. OTHERWISE the parentheses force ByVal to be used by the routine - even though ByVal was not specified in the routine. 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. 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. And FYI, ByRef is always implied in the declaration if you are silent; thus TMK ByRef has no apparent value other than documentary.

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; 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).

Again, that is to protect you from unfavorably getting ByVal from a routine. Conversely, of course, if you WANT ByVal interpretation regardless of the routine's declaration, then LEAVE OFF the Call (and use parentheses).

Incurve answered 16/6, 2019 at 6:51 Comment(6)
This argumentation is slightly off. Parentheses don't magically turn ByRef into ByVal. Parentheses force the evaluation of an expression, like they do in every single other expression context. So what happens is, the expression is evaluated, and its result is passed to the invoked procedure, which receives it ByRef as advertised - only, nothing on the caller's side is holding on to a reference to the value of the argument expression, so the net effect is similar to it being passed ByVal. I invite you to read this answer.Almonte
That's an excellent dissection of the process. Nonetheless, from a mechanical perspective, from the user's perspective, if you add "unrequired" parentheses, the argument will be treated ByVal, even if you state "ByRef" on the function itself. And that is bizarrely unintuitive to those who don't know the intricacies you pointed out. What I said was "parentheses force ByVal to be used by the routine" - which is a true statement, even though as you point out, the parentheses instigate an intermediate process, and the upshot of that process is that a ByVal treatment ensues.Incurve
As to the link you provided, it argues that Call does nothing (except facilitate certain single line multi-commands). I simply reject that as false, because myfunc(foo) and Call myfunc(foo) strictly produce two different results whenever MyFunc changes the foo argument (unless MyFunc says "ByVal").Incurve
you're missing the entire point. myFunc(foo) cannot exist, it's myFunc (foo) (that space matters!), and the correct syntax for an implicit call statement is myFunc foo, without the parentheses. You are using Call as a surrogate to proper understanding of the language's syntax.Almonte
I'm only saying that if anyone uses superfluous parentheses - and millions of people do, whether we like it or not - then Call defends them against unexpected results. OBVIOUSLY you shouldn't use parentheses when they are not required. The fact is that people do so anyway, and in those cases Call does affect foo. Anyway, congratulations for pointing out the omitted space.Incurve
The VBE (nothing to do with Excel) turns myFunc(foo) into myFunc (foo) and will keep adding that space no matter how hard you try. You're talking about a different instruction, bar = myFunc(foo), where the parentheses delimit the argument list, whereas in myFunc (foo) they enclose an argument expression.Almonte
P
3

I use Call for all VBA development of common library functions that I possibly will use in VB.NET. This allows me to move code using copy and paste between all the flavors of 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.

If you have no plans to move your VB/VBA code to VB.NET, then there is no need to use the Call statement.

Phosphorescent answered 5/4, 2010 at 14:27 Comment(0)
L
2

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. 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. Yes, that is:

Call (Await c).Disable()

That's why I love VS2017 Preview. 😄

Laval answered 5/3, 2018 at 7:40 Comment(0)
T
1

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:

"If you use either Call syntax to call any intrinsic or user-defined function, the function's return value is discarded."

This is why Call is far from useless. 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). 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. What do you do?

You could, for example, use boolean functions that return False if something goes wrong. Instead of calling a sub, call a function SupportTasks inside and If statement that will exit the Main sub if there's an anomaly:

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). 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). That's where Call comes in to save the day!

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...

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.

Tented answered 3/4, 2017 at 22:50 Comment(4)
You can simply use SupportTasks SomeArgument and it compiles just fine without the use of Call.Artiodactyl
That's true. It just ignores the return value. Guess that renders my essay slightly off then, no?...Tented
However since you're not doing an "equal sign" assignment when you use Call, you can just omit the assignment anyway and get to the same place (that the retval is disregarded). Thus, "as such", Call is only valuable from a documentary standpoint. I.e., the following may behave functionally identically: 1. call myfunc(i) 2. myfunc(i) ( BUT see my top level reply to this thread below!! Depending on the function declaration, this may produce different ByVal or ByRef treatment of variable i !! So there's yet another meaningful value to Call vs. its omission)Incurve
Every single MsgBox "foo" instruction ever written is discarding the function's return value. I've yet to see Call MsgBox("foo") in code written by someone that claims to consistently use Call.Almonte
P
0

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. 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.

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. 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
Protohistory answered 21/9, 2017 at 22:51 Comment(3)
Fun! Part of me wants to try this out, the other part of me is scared of learning another bad habit :-)Forename
@Forename I suggest just keeping it in your hat as trivia. VB creates enough bad habits as it is :)Protohistory
Yup. I'm actually using VBA and intriguing as it was (AFAICT) it just doesn't come close to working in VBA but was interesting to explore none the less. Cheers,Forename
P
-1

I never use Call but this simple and explicit rule :

Calling a Function => Always use parentheses, meaning the method called is a Function (and so indicating that a value will be returned !)

Calling a Sub => Never use parentheses, meaning the method called is a Sub (and so indicating that no result will be returned !)

Sub Main
    dim res as String
    PrintNameDefault    'Sub => no parenthesis (no result returned)
    PrintName "Mike"    'Sub => no parenthesis (no result returned)
    res = GetWelcome()  'Function => parenthesis, *result returned*
    Debug.Print res     'Sub => no parenthesis (no result returned)
    res = GetWelcomeUser("Bob")  'Function => parenthesis, *result returned*
    Debug.Print res     'Sub => no parenthesis (no result returned)
    GetWelcome          'Function call without getting the result => no parenthesis, *result returned but not used*
    GetWelcomeUser "Li" 'Function call without getting the result => no parenthesis, *result returned but not used*
End Sub

Sub PrintNameDefault()
    Debug.Print "My name is still unknowned !"
End Sub

Sub PrintName(name as String)
    Debug.Print "My name is " & name & " !"
End Sub

Function GetWelcome() as String
    GetWelcome = "Welcome in this amazing App !"
End Function

Function GetWelcomeUser(username as String) as String
    GetWelcomeUser = "Welcome back " & username & " !"
End Function
Parmentier answered 4/1, 2024 at 16:11 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.