49 votes

L'opérateur "Et" de VBA évalue-t-il le deuxième argument lorsque le premier est faux ?

Function Foo(thiscell As Range) As Boolean
  Foo = thiscell.hasFormula And (InStr(1, UCase(Split(thiscell.formula, Chr(40))(0)), "bar") > 0)
End Function

Cette fonction existe pour tester la présence d'une certaine sous-chaîne (bar, dans ce cas) avant le (.

Le cas qui me pose problème est celui où la cellule passée dans la fonction est vide, où thisCell.hasFormula est fausse, mais où l'instruction après le et est toujours évaluée. Cela me donne une erreur d'indice hors de portée au moment de l'exécution.

VBA continue-t-il réellement à évaluer le second argument de la fonction And, même si le premier est faux ?

61voto

DOK Points 21175

Ce que vous recherchez s'appelle " évaluation des courts-circuits ".

VBA ne l'a pas.

Vous pouvez voir une approche qui est probablement adaptable à votre situation aquí .

L'approche qui y a été retenue a consisté à substituer une Select Case pour le If . Il existe également un exemple d'utilisation d'éléments imbriqués Ifs .

12voto

Issun Points 7271

Comme DOK mentionné : Non, le VBA n'a pas d'évaluation de court-circuit.

Il est techniquement plus efficace d'utiliser 2 If-then au lieu d'utiliser les instructions AND mais, à moins que vous ne le fassiez souvent, vous ne remarquerez pas les économies réalisées, alors optez pour ce qui est le plus lisible. Et si vous voulez être vraiment technique, VBA gère de multiples If-then plus rapidement que les Select Case également.

VBA est excentrique :)

2voto

Hugh Allen Points 3799

VBA a un comportement semblable à un court-circuit. Normalement, Null se propage à travers les expressions, par exemple 3 + Null est Null y True And Null est Null . Cependant :

? False And Null
False

Cela ressemble à un comportement de court-circuit - que se passe-t-il ? Null ne se propage pas lorsque l'autre argument d'une conjonction ( And ) est False o 0 - le résultat est juste False o 0 . Peu importe que ce soit l'argument de gauche ou de droite. Il en va de même si l'autre argument d'une disjonction ( Or ) est True ou un nombre entier non nul (une valeur à virgule flottante sera arrondie à un nombre entier en utilisant la fonction cette règle ).

Les effets secondaires et les erreurs ne peuvent donc pas être évités dans les arguments pour And y Or mais Null la propagation peut être "court-circuitée". Ce comportement semble être hérité de SQL .

2voto

sopatt Points 41

La réponse est oui, VBA ne court-circuite pas l'évaluation.

Ce n'est pas seulement une question de style ; cela fait une grande différence dans une situation comme celle-ci :

If i <= UBound(Arr, 1) And j <= UBound(Arr, 2) And Arr(i, 1) <= UBound(Arr2, 1) Then
    Arr2(Arr(i, 1), j) = Arr(i, j)
End If

...ce qui est incorrect. C'est plus approprié :

If i <= UBound(Arr, 1) And j <= UBound(Arr, 2) Then
    If Arr(i, 1) <= UBound(Arr2, 1) Then
        Arr2(Arr(i, 1), j) = Arr(i, j)
    End If
End If

Ou si vous avez une aversion pour les "ifs" imbriqués :

If i > UBound(Arr, 1) Or j > UBound(Arr, 2) Then
    ' Do Nothing
ElseIf Arr(i, 1) > UBound(Arr2, 1) Then
    ' Do Nothing
Else
    Arr2(Arr(i, 1), j) = Arr(i, j)
End If

1voto

Andreas Dietrich Points 155

Puisque la réponse est l'une des mieux classées dans Google, il suffit de chercher quelque chose comme vba if condition not lazy Je voudrais donner un exemple plus simple, le problème et les solutions des deux conditions : AND et le plus intéressant OR ...

Dim cond1 As Boolean   'some 1st condition that may be True or False
Dim obj As Collection  'just some sample object that may or may not be instantiated

(² : Je trouve qu'il est préférable d'expliquer aux autres devs, pourquoi vous n'avez pas choisi OR s'ils ne connaissent pas le contexte)


le site AND cas

cond1 = False
If cond1 Then Set obj = New Collection

problème :

If cond1 And obj.Count > 0 Then Debug.Print "Count > 0!"  'throws error if < cond1 = False > 
                                                          'because condition 2 is always evaluated

solution :

If cond1 Then If obj.Count > 0 Then Debug.Print "Count > 0!"  'AND would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920

Selon le goût, la complexité et la lisibilité, il peut être judicieux de l'écrire de cette façon :

If cond1 Then
    If obj.Count > 0 Then  'AND would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
        Debug.Print "Count > 0!"
    End If
End If

le site OR cas

 cond1 = True
 If Not cond1 Then Set obj = New Collection  'obj stays < Nothing > otherwise

problème :

 If cond1 Or obj.Count = 0 Then Debug.Print "no objects!"  'throws error if < cond1 = True >
                                                           'because condition 2 is always evaluated

solution 1 :

une seule ligne en place, non redondante, sans GoTo en utilisant Select :

 Select Case True:  Case cond1, obj.Count = 0:  Debug.Print "no objects!":  End Select  'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920

au cas où il devrait/doit être sur plusieurs lignes et avec autre chose :

 Select Case True
     Case cond1, obj.Count = 0  'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
         Debug.Print "no objects!"
     Case Else
         Debug.Print "object count: " & obj.Count
 End Select

solution 2 :

en place, du code non redondant avec un minimum d'efforts. GoTo l'utilisation, mais plus longue If -code à plusieurs lignes :

 If cond1 Then
 noObjs:
     Debug.Print "no objects!"
 ElseIf obj.Count = 0 Then  'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
     GoTo noObjs
 End If

solution 3 :

en place, les conditions (peuvent s'adapter) sur une seule ligne, comme dans le cas de OR -concaténation avec un certain nombre GoTo l'usage :

 If cond1 Then GoTo noObjs ElseIf obj.Count = 0 Then GoTo noObjs  'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
 GoTo skipOnAllFalse
 noObjs:
     Debug.Print "no objects!"

 skipOnAllFalse:    'use more specific label/scenario name if possible

solution 4 :

code déplacé ( Sub ), en évitant GoTo Les conditions peuvent tenir sur une seule ligne, mais le code du module/de la classe peut être plus illisible/étendu/encombré :

 Private Sub noObjs():  Debug.Print "no objects!"

 If cond1 Then noObjs ElseIf obj.Count = 0 Then noObjs  'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920

solution 5 :

en utilisant une seule variable de condition :

 Dim any As Boolean:  any = cond1
 If Not any Then any = obj.Count = 0  'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
 If any Then Debug.Print "no objects!"

solution 6 :

en utilisant des variables de condition multiples :

 Dim c1 As Boolean:  Dim c2 As Boolean
 c1 = cond1
 If Not c1 Then c2 = obj.Count = 0  'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
 If c1 Or c2 Then Debug.Print "no objects!"  'safe to use Or now

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X