Voir le flux RSS

Pierre Fauconnier

[VBA] De grâce, pas d'EXIT SUB dans vos codes, svp! Même pour une gestion d'erreur!!

Noter ce billet
par , 24/02/2016 à 07h35 (1309 Affichages)
Au risque de me répéter, je le rappelle encore et encore: Pas d'EXIT SUB dans vos codes. Ce n'est pas compliqué de bien coder, tout de même. Et il existe très souvent, même toujours, une alternative à une sortie prématurée de procédure ou de fonction.

Il ne faut qu'une seule sortie de procédure ou de fonction. Pourquoi? Parce qu'avoir deux sorties de procédure, voire plus parfois, c'est risquer de sortir sans "remettre les choses à leur place"! Apparemment, difficile à comprendre, comme dans une réponse fournie dans cette discussion, où c'était pourtant très simple d'éviter Exit Sub.

Examinons le code suivant proposé dans la discussion:
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
Private Sub Worksheet_Change(ByVal Target as Range)
    If Target.Address <> "$A$1" Then Exit Sub
    Range("A2").Value = Range("A2").Value + Target.Value
End Sub

Deux sorties de Proc. Au passage, on notera l'absence de test sur la valeur numérique à ajouter à A2. Si l'utilisateur saisit une valeur textuelle, bardaf c'est l'embardée. Au mieux, selon l'option de gestion des erreurs, on remonte au code appelant (Ah zut, comme c'est une proc événementielle, il y a des chances qu'il n'y ait rien d'autre dans la pile d'appel), au pire l'utilisateur entre dans le VBE et se prend une belle ligne surlignée de jaune. Au passage (bis), on notera l'absence de désactivation des événements, ce qui va amener la procédure à être appelée à nouveau lors de la modification de A2. C'est vrai que ce n'est qu'un appel de plus qui coûte probablement moins d'une milliseconde, alors, pourquoi se gêner?

Pour ma part, j'ai eu suffisamment de fois l'envie d'étriper les "programmeurs" qui pissent du code comme un porc se vautre dans son auge que pour savoir ce que signifie "bien coder". Et je le dis sans ambages, mettre des EXIT SUB dans son code, c'est coder comme un cochon. Ca n'a rien à voir avec "le style", c'est juste sale, chi*** à maintenir, et mettre les mains dans le lisier des autres n'est pas ma tasse de thé. Malheureusement, lorsqu'il s'agit de nettoyer ce code digne d'un goret, c'est moi qu'on appelle, le goret étant parti planter des salades

Pourquoi un Exit Sub, d'ailleurs? Est-ce si compliqué d'écrire:
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
Private Sub Worksheet_Change(ByVal Target as Range)
    If Target.Address = "$A$1" Then
      Range("A2").Value = Range("A2").Value + Target.Value
    End If
End Sub

Bien sûr, le code montré ci-dessus est tellement basique que cela ne sert à rien de faire un caca nerveux! Pierre, du calme, Xanax et Attarax sont tes copains. Oui mais... Quand l'habitude de coder salement est prise, elle est prise...

Examinons le code suivant:
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
Sub MySub()
  Application.DisplayAlerts = False
  ...
  ...
  If ...then Exit Sub
  ...
  Application.DisplayAlerts = True ' <=== On n'est pas certain de passer par cette ligne!!
End Sub

Ai-je besoin de vous faire un dessin? Vous pouvez remplacer Application.DisplayAlerts en début de fonction par Application.EnableEvents=False, c'est assez "amusant" aussi à déboguer, quand vous pestez pendant une heure (ou plus) pour tenter de comprendre pourquoi votre événement n'est pas levé?

Et la gestion d'erreur?
Ah oui, la gestion d'erreur, parlons-en! Je vois souvent ceci:
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Private Sub Worksheet_Change(ByVal Target As Range)
  On Error GoTo ErrHandler
  ...
  If Target.Address = "$A$1" Then
    Application.EnableEvents = False
    ...
    Range("a2") = Range("a2") + Range("a1")
    ...
    Application.EnableEvents = True
  End If
  Exit Sub
 
EndHandler:
  ... ' <== Gestion de l'erreur
End Sub

Pouah!! Et si l'utilisateur a saisi une valeur textuelle en A1, que se passe-t-il? Il passe dans la gestion de l'erreur et hop, il sort de la procédure en laissant la gestion d'événéments à False...

Donc, une bonne façon de coder, c'est, par exemple:
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Private Sub Worksheet_Change(ByVal Target As Range)
  On Error GoTo EndHandler
  ...
  If Target.Address = "$A$1" Then
    Application.EnableEvents = False
    ...
    Range("a2") = Range("a2") + Range("a1")
    ...
  End If
 
EndHandler:
  Application.EnableEvents = True
  If Err <> 0 Then
    ...
  End If
End Sub

Ainsi, on est certain de remettre les choses à leur place avant de sortir de la procédure.


Alors, suivez mon conseil: Pas de EXIT SUB ou de EXIT FUNCTION !!

Capito?

Dans ce billet plus récent, je vous parle de la philosophie des boucles…

Allez, bon pissage de code

Envoyer le billet « [VBA] De grâce, pas d'EXIT SUB dans vos codes, svp! Même pour une gestion d'erreur!! » dans le blog Viadeo Envoyer le billet « [VBA] De grâce, pas d'EXIT SUB dans vos codes, svp! Même pour une gestion d'erreur!! » dans le blog Twitter Envoyer le billet « [VBA] De grâce, pas d'EXIT SUB dans vos codes, svp! Même pour une gestion d'erreur!! » dans le blog Google Envoyer le billet « [VBA] De grâce, pas d'EXIT SUB dans vos codes, svp! Même pour une gestion d'erreur!! » dans le blog Facebook Envoyer le billet « [VBA] De grâce, pas d'EXIT SUB dans vos codes, svp! Même pour une gestion d'erreur!! » dans le blog Digg Envoyer le billet « [VBA] De grâce, pas d'EXIT SUB dans vos codes, svp! Même pour une gestion d'erreur!! » dans le blog Delicious Envoyer le billet « [VBA] De grâce, pas d'EXIT SUB dans vos codes, svp! Même pour une gestion d'erreur!! » dans le blog MySpace Envoyer le billet « [VBA] De grâce, pas d'EXIT SUB dans vos codes, svp! Même pour une gestion d'erreur!! » dans le blog Yahoo

Commentaires

  1. Avatar de kolodz
    • |
    • permalink
    Il a effectivement un certain nombre de cas où la sortie prématuré de fonction n'est pas une bonne idée. Cependant, il me semble que ces cas sont dû à une méconnaissance du langage et donc du comportement impliqué.
    Personnellement, je vois quelque cas où la sortie de fonction peut-être intéressant. Bien qu'il soit totalement possible de faire, quelque chose d’équivalent et propre avec des sous-fonctions.

    Note : Le même principe est présent dans d'autre langage. Notamment pour des questions de libération mémoire.
  2. Avatar de Pierre Fauconnier
    • |
    • permalink
    Salut Patrick

    Citation Envoyé par kolodz
    [...]
    Personnellement, je vois quelque cas où la sortie de fonction peut-être intéressant.[...]
    Tu peux développer un peu?
  3. Avatar de patricktoulon
    • |
    • permalink
    Bonjour pierre
    il certains que exit( sub/function) peut etre evité mais (exit for) qu'en pense tu ? car perso je m'en sert tres souvent
  4. Avatar de Pierre Fauconnier
    • |
    • permalink
    Salut Patrick

    Citation Envoyé par patricktoulon
    […]mais (exit for) qu'en pense tu ? car perso je m'en sert tres souvent
    Dans ce billet plus récent, je donne un exemple où l'on peut se passer de Exit dans un For. C'est notamment le cas lorsque l'on connaît le nombre d'éléments sur lesquels on est susceptible de boucler.

    Regarde les deux fonctions suivantes: elles font exactement la même chose, à savoir récupérer une feuille XL selon son codename. La première le fait avec un For et on a un Exit pour sortir de la boucle dès que la feuille est trouvée, histoire de ne pas boucler pour rien. La seconde obtient le même résultat avec trois lignes en plus, mais sans Exit, la condition étant exprimée sur la ligne Do While. Perso, je préfère la seconde solution qui est plus directement lisible et compréhensible. L'intention du programmeur, et donc la finalité de la boucle, transparaît à la lecture du code.

    Bien sûr, dans les codes qui suivent, le code dans la boucle étant très court, je peux voir rapidement qu'il y a un Exit, mais si la boucle comprend un nombre plus important de lignes, ce sera moins aisé.

    Méthode For avec Exit
    Code VB : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Function getSheetFromCodeName1(CodeName As String, Optional wb As Workbook) As Object
      Dim sh As Object
     
      If wb Is Nothing Then Set wb = ActiveWorkbook
      For Each sh In wb.Sheets ' <=== Je ne sais pas que je risque de sortir sans avoir bouclé sur toutes les feuilles
        If StrComp(sh.CodeName, CodeName, vbTextCompare) = 0 Then
          Set getSheetFromCodeName = sh
          Exit For
        End If
      Next
    End Function

    Méthode Do While sans Exit
    Code VB : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Function getSheetFromCodeName(CodeName As String, Optional wb As Workbook) As Object
      Dim Counter As Long
      Dim Found As Boolean
     
      If wb Is Nothing Then Set wb = ActiveWorkbook
      Counter = 1
      Do While Counter <= wb.Sheets.Count And Not Found ' <=== Je comprends l'intention de sortir dès que la feuille est trouvée
        If StrComp(wb.Sheets(Counter).CodeName, CodeName, vbTextCompare) = 0 Then
          Set getSheetFromCodeName = wb.Sheets(Counter)
          Found = True
        End If
        Counter = Counter + 1
      Loop
    End Function


    Cela dit, c'est une question de style de programmation et de façon d'appréhender le code (ainsi que sa maintenance, son partage, sa relecture, …)