2017-07-06 4 views
0

Prenons l'exemple minimale suivante:C#: Lancer une exception sans écraser le moment actif un

try { foo } 
finally { bar() } 

foo peuvent ou peuvent ne pas jeter, et bar() peut ou ne peut pas jeter. Si les deux fonctions sont lancées, l'exception lancée par bar() remplacera l'exception lancée par foo. Cependant, j'ai besoin de voir à la place l'exception qui a été lancée par foo. Le seul code que je peux contrôler est celui à l'intérieur bar(). En C++, cela peut être réalisé en vérifiant std::uncaught_exceptions à bar(). Qu'en est-il de C#?

Il ya a check using Marshal.GetExceptionPointers(), mais comme la réponse l'indique, c'est un hack horrible, et dans mon cas cela ne suffit pas car mon code doit fonctionner sur Mono, qui n'implémente pas cette fonction.

Contexte:

Dans mon cas, la vie réelle, foo est un grand bloc de code qui est fourni par l'utilisateur de la bibliothèque et peut jeter toutes sortes d'exceptions, et bar() est une fonction Dispose qui est fourni par moi et appelé automatiquement.

+0

Comme vous pouvez contrôler le code dans 'bar' envelopper dans bloc try/catch si l'extérieur, vous ne verrez que les exceptions lancées par' foo'. –

Répondre

0

Vous devez coder manuellement:

// Whether an exception has been thrown from the main block. 
bool hasMainException = false; 

try 
{ 
    foo(); 
} 
catch 
{ 
    // Record that exception has been thrown from main block, and rethrow. 
    hasMainException = true; 
    throw; 
} 
finally 
{ 
    // Execute secondary block within its own try-catch block. 
    try 
    { 
     bar(); 
    } 
    catch 
    { 
     // Only rethrow exception from secondary block if there isn't 
     // already an exception from the main block. 
     if (!hasMainException) 
      throw; 
    } 
} 

Soit dit en passant, je devais obtenir quelque chose de très semblable à votre cas d'utilisation impliquant Dispose méthodes qui pourraient jeter, j'ai donc écrit quelques méthodes d'extension pour y parvenir, plus une variété de stratégies pour soutenir tous les cas d'utilisation. Voir DisposableExtensions et DisposeExceptionStrategy. La stratégie qui vous intéresse est Subjugate.

+0

Comme je l'ai écrit, malheureusement, le seul code que je peux contrôler est celui de 'bar()', tout le reste est la responsabilité de l'utilisateur de la bibliothèque ... –

+0

@mic_e: Vous avez raison; J'ai manqué ça. Je ne crois pas que cela puisse être fait autrement qu'avec le hack que vous avez mentionné. – Douglas

0

Ce sera

  • jeter l'exception de foo, même si une exception est levée dans bar.
  • jeter l'exception de bar s'il n'y avait aucune exception de foo.
  • ne lance rien si rien ne déclenche une exception.
  • préserver la pile d'origine de l'une ou l'autre exception.

    ExceptionDispatchInfo exceptionInfo = null; 
        try 
        { 
         foo(); 
        } 
        catch(Exception ex) 
        { 
         exceptionInfo = ExceptionDispatchInfo.Capture(ex); 
        } 
        finally 
        { 
         try 
         { 
          bar(); 
         } 
         catch(Exception ex) 
         { 
          exceptionInfo = exceptionInfo ?? ExceptionDispatchInfo.Capture(ex); 
         } 
        } 
        exceptionInfo?.Throw();