2 votes

Comment gérer toutes les exceptions dans une classe C# où le ctor et le finalizer lèvent des exceptions ?

Comment puis-je traiter toutes les exceptions pour une classe similaire à la suivante dans certaines circonstances ?

class Test : IDisposable {
  public Test() {
    throw new Exception("Exception in ctor");  
  }
  public void Dispose() {
    throw new Exception("Exception in Dispose()");
  }
  ~Test() {
    this.Dispose();
  }
}

J'ai essayé ça mais ça ne marche pas :

static void Main() {
  Test t = null;
  try {
    t = new Test();
  }
  catch (Exception ex) {
    Console.Error.WriteLine(ex.Message);
  }

  // t is still null
}

J'ai également essayé d'utiliser "using" mais il ne gère pas l'exception levée par ~Test() ;

static void Main() {
  try {
    using (Test t = new Test()) { }
  }
  catch (Exception ex) {
    Console.Error.WriteLine(ex.Message);
  }
}

Avez-vous une idée de la façon dont je peux contourner ce problème ?

5voto

Dan Bryant Points 19021

Tout d'abord, un finaliste doit jamais lancer une exception. Si c'est le cas, c'est que quelque chose a mal tourné et l'application doit se planter brutalement. Un Finalizer ne doit jamais appeler directement Dispose(). Les Finalizers ne servent qu'à libérer les ressources non gérées, car les ressources gérées peuvent même ne pas être dans un état valide lorsque le Finalizer s'exécute. Les références gérées seront déjà nettoyées par le ramasseur d'ordures, vous devez donc les éliminer dans votre Dispose, et non dans votre Finalizer.

Cela dit, une exception dans Dispose devrait être attrapée si vous appelez Dispose explicitement. Je ne comprends pas bien comment le cas "using" n'a pas déclenché l'exception. Cela dit, Dispose ne devrait pas non plus lancer d'exceptions, si vous pouvez l'éviter. En particulier, un Dispose qui lève une exception après un bloc using va " écraser " toute exception qui pourrait se produire dans le bloc using avec l'exception Dispose.


Quelques documents de référence supplémentaires aquí

3voto

John Saunders Points 118808

Je pense qu'une partie de la réponse est que vous ne devriez pas gérer les exceptions dans ces cas.

Vous ne devriez attraper les exceptions que si vous pouvez les récupérer, ou si vous pouvez ajouter des informations supplémentaires à l'exception et la relancer. Vous ne devez pas attraper toutes les exceptions. Laissez le code situé plus haut dans la pile d'appels gérer autant d'exceptions que possible.

1voto

Brian Gideon Points 26683

J'ai quelques observations à faire.

Premièrement, évitez de lancer des exceptions à partir de Dispose. En fait, j'irais presque jusqu'à dire jamais. Les développeurs .NET ont été conditionnés pour s'attendre à ce que Dispose réussisse toujours, et pour une bonne raison. Il serait gênant d'envelopper l'appel dans un try-catch à chaque fois et cela réduirait certainement la lisibilité.

Deuxièmement, et c'est une question qui est souvent débattue, évitez de lancer des exceptions à partir des constructeurs. Les exceptions liées à la validation de l'état, comme ArgumentException ou IndexOutOfRangeException, sont acceptables car elles sont généralement générées en raison de violations de conditions préalables et résultent habituellement d'une erreur de programmation. Mais les exceptions imprévisibles telles que SqlException obligeraient l'appelant à envelopper le constructeur dans un bloc try-catch. Encore une fois, cela conduit à des scénarios de codage gênants. Mais, plus important encore, cela peut conduire à des fuites de ressources subtiles dans des scénarios où le constructeur alloue des ressources non gérées puis lève une exception avant de retourner la nouvelle instance à l'appelant. Sans la référence de l'instance, l'appelant ne peut pas appeler Dispose.

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