49

I'm trying to get a lazy evaluation with 'And' in my Excel macro by doing the following:

If Not myObject Is Nothing *And* myObject.test() Then
    'do something'
Else
    'do something else'
End If

I know lazy evaluation exists in VB.NET as AndAlso and OrElse but cannot find anything similar in VBA. If lazy evaluation does not exist in VBA, what's the best way to structure the code so that it will evaluate the way I expect?

Jake88
  • 955
  • 1
  • 19
  • 39
Luis
  • 1,210
  • 2
  • 11
  • 24
  • 9
    VBA like plain VB6 does not have short-circuit evaluation – Mitch Wheat Jul 14 '10 at 00:36
  • Thanks, I solved my problem without a solution to this but I'm still curious as to what to do for this situation. I wouldn't be surprised if I'm missing something really obvious but I can't find a good way to do this without rewriting code, i.e. have the same code in two different else cases. – Luis Jul 14 '10 at 01:28

10 Answers10

56

The only short circuiting (of a sort) is within Case expression evaluation, so the following ungainly statement does what I think you're asking;

Select Case True
    Case (myObject Is Nothing), Not myObject.test()
        MsgBox "no instance or test == false"
    Case Else
        MsgBox "got instance & test == true"
    End Select
End Sub
Monolo
  • 18,205
  • 17
  • 69
  • 103
Alex K.
  • 171,639
  • 30
  • 264
  • 288
  • Just for clarity, the comma in a Case act kinda like an "OrElse", but it checks conditions from left to right instead of right to left. – Wildhorn Jan 31 '20 at 19:19
  • 7
    @Wildhorn: "instead of right to left" - that sounds as if `OrElse` in VB.NET evaluates the conditions from right to left. Is that what you meant? Because I don't think that's the case – ElRudi Mar 30 '20 at 10:52
16

This is an old question, but this issue is still alive and well. One workaround I've used:

Dim success As Boolean       ' False by default.

If myObj Is Nothing Then     ' Object is nothing, success = False already, do nothing.
ElseIf Not myObj.test() Then ' Test failed, success = False already, do nothing.
Else: success = True         ' Object is not nothing and test passed.
End If

If success Then
    ' Do stuff...
Else
    ' Do other stuff...
End If

This basically inverts the logic in the original question, but you get the same result. I think it's a cleaner solution than the others here that only use If statements. The solution using a Select statement is clever, but if you want an alternative using only If statements, I think this is the one to use.

neizan
  • 2,291
  • 2
  • 37
  • 52
3

Or you could create a function that takes your object as a parameter and returns boolean for either case. That's what I usually to.

i.e.

if Proceed(objMyAwesomeObject) then
       'do some really neat stuff here
else
       'do something else, eh
end if
...
end sub

private function Proceed(objMyAwesomeObject as Object)
     if not objMyAweseomeObject is nothing then
            Proceed = true
     elseif objMyAwesomeObject.SomeProperty = SomeValue then
            Proceed = true
     else
            Proceed = false
     endif
end function
William
  • 31
  • 1
  • 1
    I don't think this will work. If `Not objMyAwesomeObject Is Nothing` evaluates to `True`, then the first `ElseIf` statement will never execute. However, if `Not objMyAwesomeObject Is Nothing` = False (`objMyAwesomeObject Is Nothing` = True), then the first `ElseIf` statement will execute and will raise an error because you're trying to access a property on `objMyAwesomeObject` which `Is Nothing`. – neizan Dec 12 '13 at 09:59
1

Improving on this answer to a different question about the same basic problem, here is what I chose to do:

dim conditionsValid as boolean

conditionsValid = Not myObject Is Nothing
if conditionsValid then conditionsValid = myObject.test()
if conditionsValid then conditionsValid = myObject.anotherTest() 

if conditionsValid then
   'do something'
else
   'do something else'
end if

I think this code is clearer than the other answers that have been suggested, and you (usually) don't need a different variable for each validation, which is the improvement over the original answer to the other question. By the way, each new condition you need adds just one more line of code.

carlossierra
  • 4,479
  • 4
  • 19
  • 30
  • the boolean is just set to the wrong value in your code. This is correct: `conditionsValid = Not myObject Is Nothing`. The proposed action 'do something else' would rather be 'do error handling', in your example both actions sound identical. YMMV. – user1016274 Jun 23 '23 at 08:50
  • You are right @user1016274. I just fixed it. Thanks – carlossierra Jun 24 '23 at 13:50
0

Since the following syntax works

If myObject.test() Then do something

Then the one liner syntax could be used to short circuit the evaluation. Below, the first If statement ensures that myObject is something. Otherwise, it won't even try to evaluate the second If.

If Not myObject Is Nothing Then If myObject.test() Then
    'do something'
Else
    'do something else'
End If

Of course, if you want 'do something else' if myObject Is Nothing, then this may not work.


Update 2020/06/30

After a comment pointed out that this answer didn't work, I have verified that the syntax does not appear to work in modern VBA. Leaving original answer for legacy purposes.

Cohan
  • 4,384
  • 2
  • 22
  • 40
  • I see you used two `If`s here, one for `Not myObject Is Nothing`, and the second one for `myObject.test()`. But, as the first `If` statement is in the **one liner** format, the content of the first `If` (which is the second `If`) **cannot extend to more than one line**. – Sreenikethan I Jun 03 '19 at 10:29
  • I have removed the first code snippet, because it demonstrates an one-liner `If` statement. I have explained in my previous comment that one-liner `If`s cannot span multiple lines! :D – Sreenikethan I Jun 03 '19 at 10:32
  • Thanks for the suggested edit but the syntax in the answer was deliberate. I updated my answer to better reflect that. – Cohan Jun 03 '19 at 15:15
  • 1
    The example doesn't even compile. How can someone upvote this answer? – Olli Jun 30 '20 at 14:45
  • Because five years ago it did compile. But I just tried it out (it's been a while since I've bothered with VBA) and I agree, that it is not working as it used to. – Cohan Jun 30 '20 at 14:54
  • @Cohan Actually, this won't compile on any version of VBA that I'm aware of. I'm not sure in what environment you tested it but it doesn't compile in Office 2019, 2010, or even Office 2003. It also doesn't compile in VB6 (1998). – 41686d6564 stands w. Palestine Jun 30 '20 at 15:18
  • 2
    @Cohan But it would be nice this would work ;) Thanx for your quick answer and correction. The ways of VBA are mysterious. – Olli Jun 30 '20 at 16:52
  • It also doesn't compile in Excel 365 2016 (VBA 7.1) – golimar Jul 15 '20 at 14:13
0
If Not myObject Is Nothing Then
    If myObject.test() Then
        'do something'
    End If
Else
   'do something else'
End If

I think that's the way you have to do it.

Edit

Maybe like this

Dim bTestsFailed as Boolean
bTestsFailed = False

If Not myObject Is Nothing Then
    If myObject.test() Then
        'do something'
    Else
        bTestsFailed = True
    End If
Else
   bTestsFailed = True
End If

If bTestsFailed Then
    'do something else
End If

Isn't VBA great?

Dick Kusleika
  • 32,673
  • 4
  • 52
  • 73
  • 1
    I think he wants to have the else condition also execute if myObject Is Nothing, meaning you have to duplicate the `Else 'do something else'`. – BenV Jul 14 '10 at 03:19
  • @BenV, @Dick I took the liberty of editing the answer to (I believe) the correct code. I agree with BenV – MarkJ Jul 14 '10 at 08:11
  • @MarkJ: I think he also wants `something else` to happen if myObject is not Nothing but `myObject.test()` returns False. – BenV Jul 14 '10 at 13:26
  • Thanks everyone, you're right I needed the code to execute if either was false. This works but it's a bit ugly me thinks; I like the 'Select Case' solution instead for cleanliness. – Luis Jul 14 '10 at 16:39
0

A trick around missing values may help:

Dim passed, wrongMaxPercent, wrongPercent, rightMinPercent, rightPercent
wrongPercent = 33
rightPercent = 55

'rightMinPercent = 56
wrongMaxPercent = 40

passed = (Len(wrongMaxPercent) = 0 Or wrongPercent < wrongMaxPercent) And _
         (Len(rightMinPercent) = 0 Or rightPercent >= rightMinPercent)
W. Nema
  • 189
  • 1
  • 8
  • I believe your code is part of another program... could you generalize your code? (that is, to make it into a form which can apply to any situation) – Sreenikethan I Jun 03 '19 at 10:35
0

One can switch the logical condition, working with the Or operator and switch off error messages like this:

Err.Clear
On Error Resume Next
If myObject Is Nothing Or Not myObject.test() Then
    Err.Clear
    'do something else'
Else
    'do something'
End If
On Error Goto 0 ' or On Error Goto ErrorHandler (depending on previous setting in the code)

The test for Nothing is not necessary - it only serves to clarify what is meant.

Olli
  • 327
  • 2
  • 9
0

I don't know any equivalent for OrElse, but there is a limited but useful solution for AndAlso. The following two are equivalent:

  • vb.net: If Condition1 AndAlso Condition2 Then DoSomething
  • vba: If Condition1 Then If Condition2 Then DoSomething

The are two limitations:

  1. It only works as a one liner, cannot be used to start an if-block
  2. The Else block is executed if the first condition is true and the second is false, not when the first condition is false

Even considering these two fairly crippling limitations, I often use this little trick when I don't have an Else block.

Here is an example:

Sub Test()
  Dim C As Collection

  ' This is what I often use
  If Not C Is Nothing Then If C.Count Then DoSomethingWith C

  ' Here are other usages I stay away from, because of bad readability
  If Not C Is Nothing Then If C.Count Then Debug.Print "not empty" Else Debug.Print "empty or nothing"
  Set C = New Collection
  If Not C Is Nothing Then If C.Count Then Debug.Print "not empty" Else Debug.Print "empty or nothing"
  C.Add 1
  If Not C Is Nothing Then If C.Count Then Debug.Print "not empty" Else Debug.Print "empty or nothing"
End Sub```

stenci
  • 8,290
  • 14
  • 64
  • 104
0

We might want to say: If XX < 7 Then XX = 19 but XX might be Null, so we have to test.

We can do this: If Switch ( IsNull( XX ) , True, True, XX < 7 ) Then XX = 19 So, if XX is Null, we drop through to do the assignment, otherwise only do it if the test, XX < 7, is True.

EdG
  • 1
  • 1