Version imprimable       Envoyer     
Cliquez pour évaluer et commenter
MSDN
MSDN Library
Articles Techniques
Visual Studio 2005
Visual Basic
 Gestion des erreurs sous Visual Bas...

  Passer à l'affichage pour faible bande passante
Gestion des erreurs sous Visual Basic .NET
Paru le 25 octobre 2004

Dans les précédentes versions de VB, la meilleure façon de gérer les erreurs était de placer des "On Error Goto" dans chaque routine. Mais cette instruction n'existe plus dans VB.NET. Cet article montre comment utiliser les fonctionnalités de VB.NET pour gérer les erreurs sans utiliser On Error Goto.

Sur cette page

Capturer les Exceptions Capturer les Exceptions
Lever les Exceptions Lever les Exceptions
Capturer les Exceptions spécifiques Capturer les Exceptions spécifiques

Trois types d'erreur peuvent survenir dans vos programmes :

  • Les erreurs anticipées : ce sont les erreurs que votre programme peut anticiper, comme tenter de lire un fichier qui n'existe pas, ou tenter d'ouvrir une connexion avec une chaîne de connexion invalide.

  • Les erreurs non anticipées : ce sont les erreurs qui surviennent suite à des conditions inattendues, telles qu'une erreur de programmation ou une erreur de donnée.

  • Les violations de règles métier : ce peuvent être des erreurs d'entrée de données telles que la saisie de caractères alphabétiques dans un champ numérique, ou ce peuvent être des problèmes de logique métier plus complexes comme tenter de supprimer une ligne de commande pour une commande qui aurait déjà été envoyée.

Les versions d'origine de Visual Basic fournissaient l'instruction "On Error Goto" pour capturer et gérer toutes les erreurs afin que le code puisse les récupérer. Pour les erreurs non anticipées, le "On Error Goto" pouvait intercepter l'erreur et permettre au code de se terminer en douceur, sans que l'utilisateur ait à subir un message d'erreur. Pour les violations de règles métier, le code pouvait générer un numéro d'erreur spécifique et l'instruction On Error Goto pouvait alors intercepter l'erreur et afficher un message convivial.
Cependant, On Error Goto a ses limites. Sa syntaxe de style Goto rend vos routines structurellement complexes. Et si vous oubliez de sortir de la routine avant la gestion de l'erreur tout en bas, il était facile de tomber accidentellement du code de la routine dans la gestion de l'erreur. Il était difficile de garder un code propre pouvant exécuter tous les cas (sans se soucier de savoir si une erreur survenait ou non).
Visual Basic .NET possède une riche palette de fonctionnalités qui fournissent toutes les fonctionnalités de On Error Go sans ses limitations. En fait, Visual Basic .NET supporte On Error Goto à travers la bibliothèque Microsoft Visual Basic .NET Compatibility. Cette bibliothèque permet de conserver certaines fonctionnalités de Visual Basic 6.0 en Visual Basic .NET afin de simplifier le processus de migration. Les fonctionnalités de cette bibliothèque ne doivent être utilisées que pour la migration.

Capturer les Exceptions

Dans le vocabulaire .NET, les erreurs ne sont plus appelées des erreurs mais des exceptions. Les erreurs anticipées, non anticipées et les violations de règles métier sont toutes considérées comme des exceptions. Après avoir écrit une routine, il est toujours de bon ton de penser aux exceptions que la routine pourrait provoquer (erreurs anticipées), aux exceptions inattendues que la routine pourrait générer (erreurs non anticipées) et à toutes les règles métier que la routine pourrait violer.
Par exemple, de nombreuses applications utilisent un formulaire ou une page de login pour contrôler l'accès à l'application et à ses fonctions. Le code pour valider le login est ainsi exécuté quand l'utilisateur clique sur le bouton login :

Private Sub cmdLogin_Click(ByVal sender As Object, _
      ByVal e As System.EventArgs) Handles cmdLogin.Click
   Dim oUser As User()
   Dim bValid as Boolean
   oUser = New User()
   bValid = oUser.ValidateLogin(txtUserName.Text, txtPassword.Text)
   If bValid then
      DialogResult = DialogResult.OK
   End If
   oUser.Dispose
   oUser = Nothing
End Sub

Ce code crée une nouvelle instance de la classe User, puis appelle la méthode ValidateLogin pour accéder à la base de données et valider le nom et le mot de passe entrés par l'utilisateur. Si le login est valide, il positionne DialogResult à OK pour fermer le formulaire de login. Il se débarrasse alors de l'instance de la classe User et retourne.
Il y a plusieurs endroits dans ce code où une exception pourrait survenir. La ligne de code qui crée la nouvelle instance à partir de la classe User pourrait générer une erreur non anticipée si l'instance ne pouvait pas être créée pour une raison quelconque. La méthode ValidateLogin pourrait générer des exceptions anticipées (comme une chaîne de connexion invalide), des exceptions non anticipées (comme un champ de table ou une procédure stockée manquante), ou des violations de règle métier (comme passer un nom d'utilisateur vide).
Au lieu d'ajouter un On Error Goto pour capturer ces exceptions, on peut utiliser un bloc Try/Catch .NET. La syntaxe Try/Catch facilite la capture et le traitement des exceptions de manière structurée. C'est la raison pour laquelle la gestion des exceptions .NET est souvent mentionnée comme une gestion des exceptions structurée (structured exception handling, SEH).
Un bloc Try/Catch pourrait être ajouté au code de la manière suivante :

Private Sub cmdLogin_Click(ByVal sender As Object, _
      ByVal e As System.EventArgs) Handles cmdLogin.Click
   Dim oUser As User()
   Dim bValid as Boolean
 Try
   oUser = New User()
   bValid = oUser.ValidateLogin(txtUserName.Text, txtPassword.Text)
   If bValid then
      DialogResult = DialogResult.OK
   End If
   oUser.Dispose
   oUser = Nothing
 Catch ex As Exception
   MessageBox.Show(ex.Message)
 End Try
End Sub

Si une exception intervient dans le bloc Try, le bloc Catch capture l'exception et le code à l'intérieur du bloc Catch s'exécute. Dans ce cas, la capture va saisir chaque exception, assigner l'exception à la variable ex, et afficher une boîte de message contenant le message d'exception. Si aucune exception ne survient, le bloc de code Catch est ignoré.
Si vous examinez l'exemple précédent, vous noterez que le code pour se débarrasser de l'instance ne sera pas exécuté si une exception intervient. Pour corriger cela, le code pourra être répété dans le bloc Catch, mais cela signifie qu'il y a duplication de code.
Une meilleure approche pourrait être d'utiliser le bloc optionnel Finally à l'intérieur du bloc Try/Catch de la manière suivante :

Private Sub cmdLogin_Click(ByVal sender As Object, _
      ByVal e As System.EventArgs) Handles cmdLogin.Click
   Dim oUser As User()
   Dim bValid as Boolean
 Try
   oUser = New User()
   bValid = oUser.ValidateLogin(txtUserName.Text, txtPassword.Text)
   If bValid then
      DialogResult = DialogResult.OK
   End If
 Catch ex As Exception
   MessageBox.Show(ex.Message)
 Finally
   oUser.Dispose()
   oUser = Nothing
 End Try
End Sub

Le code dans le bloc Finally sera exécuté après l'exécution complète du bloc Try, après l'exécution du bloc Catch, ou après l'exécution de toutes instructions Return dans les blocs Try ou Catch. Tout code qui a besoin d'être exécuté avant de quitter la routine doit être ajouté au bloc Finally.
Notez la manière dont la déclaration de l'objet User a été faite en dehors du bloc Try. C'est indispensable si la variable objet doit être accessible à la fois à partir du bloc Try et du bloc Finally parce que .NET a des variables de portée bloc.
Dans les précédentes versions de Visual Basic, il y avait trois types de portée pour les variables :

  • Les variables globales accessibles par l'application entière

  • Les variables module accessibles dans le fichier code (form, classe ou module) dans lequel elles étaient déclarées

  • Les variables locales accessibles seulement dans la routine dans laquelle elles étaient déclarées.

En .NET, il y a un quatrième type de portée : la portée bloc. Les variables déclarées à l'intérieur d'un bloc tel qu'un bloc Try ou un bloc For/Next ne sont accessibles qu'à l'intérieur du bloc.
Si la déclaration de l'objet User a été faite à l'intérieur du bloc Try, le bloc Finally ne pourra pas faire référence à la variable. Les variables objets qui auront besoin d'être accédées dans le bloc Finally devront être déclarées en dehors du bloc Try.

Lever les Exceptions

Une des raisons pour lesquelles les exceptions ne sont pas appelées des erreurs est que le terme erreur laisse entendre en général une faute de programmation. Une exception est une violation des hypothèses implicites d'une routine. Le framework .NET va lever des exceptions dans votre application si votre code viole une quelconque hypothèse implicite du framework .NET. Par exemple, le framework .NET suppose qu'un diviseur ne peut pas être zéro. Si votre code tente de diviser par 0, une exception sera levée. Vous pouvez alors capturer ces exceptions en utilisant le bloc Try/Catch.
Lorsque vous écrivez vos routines, vous devez suivre les mêmes règles et lever des exceptions quand une hypothèse implicite est violée. Pour lever une exception, utilisez l'instruction Throw et lever une nouvelle instance de la classe exception appropriée (voir l'aide en ligne pour la liste des exceptions .NET que vous pouvez lever).
Par exemple, la méthode ValidateLogin fait l'hypothèse qu'elle devrait recevoir en paramètres des valeurs non vides. Si les valeurs sont vides, elle doit lever une exception ArgumentOutOfRange.

Public Function ValidateLogin(ByVal sUserName As String, _
      ByVal sPassword As String) As Boolean
   If sUserName.length=0 OrElse sPassword.Length=0 Then
      Throw New ArgumentOutOfRangeException("Username and password are required.")
   End If
   ' Code to validate login here
   Return True
End Function

L'instruction Throw crée une nouvelle instance de ArgumentOutOfRangeException et définit le texte du message. Quand cette instruction est exécutée, l'exception est levée.
Le code qui suit l'instruction Throw n'est pas exécuté, la runtime .NET cherche plutôt un bloc Try/Catch. Si le code en cours n'est pas un bloc Try, la runtime .NET examine la pile d'appels pour voir si le code qui a appelé cette méthode n'est pas un bloc Try. Si elle trouve un bloc Try, elle cherche alors un bloc Catch associé. Si la runtime .NET trouve un bloc Try/Catch approprié, elle exécute alors le code dans le bloc Catch. Sinon, elle affiche le message d'exception non gérée et termine l'application.
A noter que comme la runtime .NET cherche des blocs Try associés de la pile d'appels, elle exécutera tout code dans le bloc Finally associé avant de continuer la pile d'appels.
En plus de lever des exceptions .NET, vous pouvez vouloir définit vos propres exceptions. Dans l'exemple de login, en plus de lever ArgumentOutOfRangeException, vous pouvez vouloir lever une exception spécifique si le nom d'utilisateur n'est pas valide et une exception spécifique différente si le mot de passe est invalide.
Pour créer une exception spécifique, vous pouvez créer votre propre classe d'exception. Pour s'assurer qu'elle se comporte comme une exception .NET, votre nouvelle classe d'exception doit hériter d'une des classes d'exception .NET. La classe recommandée à utiliser pour votre héritage est la classe ApplicationException . Par exemple, la classe UsernameNotFoundException devrait ressembler à cela :

Public Class UsernameNotFoundException : Inherits ApplicationException
   Public Sub New()
      MyBase.New()
   End Sub
   Public Sub New(ByVal message As String)
      MyBase.New(message)
   End Sub
   Public Sub New(ByVal message As String, ByVal innerEx As Exception)
      MyBase.New(message, innerEx)
   End Sub
End Class

En utilisant l'héritage, la nouvelle classe a automatiquement toutes les propriétés et les méthodes de la classe héritée. Ainsi, la classe UsernameNotFoundException a toutes les propriétés de la classe standard ApplicationException, comme les messages et la pile d'appels.
La nouvelle classe n'hérite d'aucun des constructeurs de la classe héritée ; d'où le besoin pour cette classe d'avoir ses propres constructeurs. La classe ApplicationException supporte trois constructeurs :

  • Un sans paramètre

  • Un avec juste le paramètre message

  • Un avec à la fois le message et une exception interne

Le dernier constructeur est utilisé dans le cas où le code capture une exception et la lève à son tour comme une autre exception, mais veut conserver les informations de l'exception d'origine.
Vous pouvez aussi ajouter vos propres propriétés et méthodes à votre nouvelle classe d'exception. Par exemple, en ajoutant une propriété username à votre classe d'exception vous pouvez journaliser toutes les exceptions et inclure le nom de l'utilisateur qui a eu l'exception. La routine peut lever des exceptions spécifiques de la manière suivante.

Public Function ValidateLogin(ByVal sUserName As String, _
      ByVal sPassword As String) As Boolean
   If sUserName.length=0 OrElse sPassword.Length=0 Then
      Throw New ArgumentOutOfRangeException("Username and password are required.")
   End If
   ' Code to locate the user record here
   If iUserRecordID = 0 Then
      Throw New UsernameNotFoundException("Invalid username")
   End If
   ' Code to retrieve the password from the user record here
   If sPassword <> sUserRecordPassword Then
      Throw New PasswordInvalidException("Invalid password")
   End If
   Return True
End Function

Cette routine lève à la fois une exception .NET et une exception spécifique si une quelconque hypothèse implicite de la routine est violée.
Notez qu'ici, il n'y a pas de bloc Try/Catch autour du code. Vous trouverez que la plupart de vos méthodes n'ont pas besoin de blocs Try/Catch. Au lieu de cela, tout votre code de procédure événement sera votre ligne de défense, capturant toutes les exceptions levées par des méthodes appelées par ces procédures événement.

Capturer les Exceptions spécifiques

Le bloc Try/Catch montré en début d'article fournit un gestionnaire d'exception générique utilisant un filtre d'exception générique "ex As Exception". Ce filtre pourrait capturer n'importe quelle exception .NET ou exception spécifique qui hériterait d'une exception .NET. Dans l'exemple de login, le filtre d'exception générique pourrait correctement capturer toute exception levée par la méthode ValidateLogin.
Il y a des cas cependant où le code a besoin d'exécuter des processus différents selon l'exception levée.

Private Sub cmdLogin_Click(ByVal sender As Object, _
      ByVal e As System.EventArgs) Handles cmdLogin.Click
   Dim oUser As User()
   Dim bValid as Boolean
 Try
   oUser = New User()
   bValid = oUser.ValidateLogin(txtUserName.Text, txtPassword.Text)
   If bValid then
      DialogResult = DialogResult.OK
   End If
 Catch ex As UsernameNotFoundException
   MessageBox.Show(ex.Message)
   txtUserName.Focus()
 Catch ex As PasswordInvalidException
   MessageBox.Show(ex.Message)
   txtPassword.Focus()
 Catch ex As Exception
   MessageBox.Show(ex.Message)
 Finally
   oUser.Dispose()
   oUser = Nothing
 End Try
End Sub

L'ordre de ces filtres d'exception est important. Les filtres les plus spécifiques doivent toujours être définis avant les filtres génériques. Le filtre le plus générique (ex as Exception) doit toujours être le dernier filtre pour s'assurer que toute exception non anticipée soit capturée.
La gestion des exceptions dans Visual Basic a changé, oui mais elle s'est améliorée. Vous pouvez construire maintenant des gestionnaires d'exception structurés pour capturer tous types d'erreur ou de violations de règles métier. Avec Try/Catch/Finally et la capacité à hériter vos propres classes d'exception à partir des exception .NET, vous pouvez jeter aux oubliettes les On Error Goto !

© 2009 Microsoft Corporation. Tous droits réservés. Conditions d'utilisation  |  Marques  |  Confidentialité
Page view tracker