Question

à beaucoup se référant de la documentation sur le net, en particulier sur le SO, par exemple: Quelle est la bonne façon de re-jeter une exception en C #? il devrait y avoir une différence entre « un jet e; » et "jeter;".

Mais, à partir de: http://bartdesmet.net /blogs/bart/archive/2006/03/12/3815.aspx ,

ce code:

using System;

class Ex
{
   public static void Main()
  {
  //
  // First test rethrowing the caught exception variable.
  //
  Console.WriteLine("First test");
  try
  {
     ThrowWithVariable();
  }
  catch (Exception ex)
  {
     Console.WriteLine(ex.StackTrace);
  }

  //
  // Second test performing a blind rethrow.
  //
  Console.WriteLine("Second test");
  try
  {
     ThrowWithoutVariable();
  }
  catch (Exception ex)
  {
     Console.WriteLine(ex.StackTrace);
  }
}

 private static void BadGuy()
 {
   //
   // Some nasty behavior.
  //
   throw new Exception();
 }

   private static void ThrowWithVariable()
 {
   try
   {
         BadGuy();
   }
  catch (Exception ex)
  {
     throw ex;
  }
}

   private static void ThrowWithoutVariable()
{
  try
  {
     BadGuy();
  }
  catch
  {
     throw;
  }
   }
}

donne le résultat suivant:

$ /cygdrive/c/Windows/Microsoft.NET/Framework/v4.0.30319/csc.exe Test.cs
Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1
Copyright (C) Microsoft Corporation. All rights reserved.

$ ./Test.exe
First test
   at Ex.ThrowWithVariable()
   at Ex.Main()
Second test
   at Ex.ThrowWithoutVariable()
   at Ex.Main()

qui est en totale contradiction avec le billet de blog.

Le même genre de résultat est obtenu avec le code de: http://crazorsharp.blogspot.com/2009/08/rethrowing-exception-without-resetting.html

Question originale : ce que je fais mal

UPDATE : même résultat avec .Net 3.5 / csc.exe 3.5.30729.4926

SUMUP :. Toutes vos réponses ont été formidables, merci encore

Ainsi, la raison est inline efficacement à cause des vacillements 64 bits.

Je devais choisir une seule réponse, et voici pourquoi je l'ai choisi LukeH réponse:

  • il devina le problème inline et le fait qu'il peut être lié à mon architecture 64 bits,

  • il a fourni le drapeau NoInlining qui est la façon la plus simple d'éviter ce comportement.

Toutefois, cette question s'élève maintenant une autre question: est-ce compatible comportement avec toutes les spécifications .Net: les CLR et ceux du langage de programmation C #

UPDATE : cette optimisation semble conforme selon la: Jeter VS rethrow: même résultat (merci 0xA3 )

Merci d'avance pour votre aide.

Était-ce utile?

La solution

Je ne peux pas reproduire le problème -. En utilisant .NET 3.5 (32 bits) me donne les mêmes résultats décrits dans l'article de Bart

Je suppose que le compilateur / gigue .NET 4 - ou peut-être est le compilateur 64 bits / gigue si cela se passe sous 3,5 trop - est inline la méthode BadGuy dans les méthodes d'appel. Essayez d'ajouter MethodImpl attribut à BadGuy et voir si cela fait une différence:

[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
private static void BadGuy()
{
    //
    // Some nasty behavior.
    //
    throw new Exception();
}

Autres conseils

J'ai essayé d'exécuter ce code moi-même et les travaux de construction de débogage comme je m'y attendais, mais je suis arrivé le même résultat que vous dans la version release.

Je soupçonne que ce qui se passe est que le compilateur inline a simplement remplacé l'appel avec un jet new Exception(); BadGuy () parce que c'est la seule déclaration BadGuy ().

Si vous désactivez l'option « code Optimize » dans les propriétés du projet -.> Écran Générer, puis à la fois la construction des rejets et de débogage produit le même résultat qui montre Badguy () en haut de la trace de la pile

Il semble que les JIT optimiseurs fait un travail ici. Comme vous pouvez le voir, la pile d'appels dans le second cas est différent de celui dans le premier cas lorsque vous exécutez la construction de débogage. Cependant, dans la construction de presse, les deux piles d'appels sont identiques en raison de l'optimisation.

Pour voir que cela est lié à la gigue, vous pouvez décorer les méthodes avec MethodImplAttribute attribut:

[MethodImpl(MethodImplOptions.NoOptimization)]
private static void ThrowWithoutVariable()
{
    try
    {
        BadGuy();
    }
    catch
    {
        throw;
    }
}

Notez que l'IL est encore différent pour ThrowWithoutVariable et ThrowWithVariable:

.method private hidebysig static void  ThrowWithVariable() cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  .locals init ([0] class [mscorlib]System.Exception ex)
  .try
  {
    IL_0000:  call       void Ex::BadGuy()
    IL_0005:  leave.s    IL_000a
  }  // end .try
  catch [mscorlib]System.Exception 
  {
    IL_0007:  stloc.0
    IL_0008:  ldloc.0
    IL_0009:  throw
  }  // end handler
  IL_000a:  ret
} // end of method Ex::ThrowWithVariable

.method private hidebysig static void  ThrowWithoutVariable() cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  .try
  {
    IL_0000:  call       void Ex::BadGuy()
    IL_0005:  leave.s    IL_000a
  }  // end .try
  catch [mscorlib]System.Object 
  {
    IL_0007:  pop
    IL_0008:  rethrow
  }  // end handler
  IL_000a:  ret
} // end of method Ex::ThrowWithoutVariable

Mise à jour pour répondre à votre question de suivi que ce soit conforme à la spécification CLI

En fait, il conforme, à savoir permettre au compilateur JIT pour activer les optimisations importantes. Annexe F des états de la page 52 (d'accentuation par moi):

  

Certaines instructions du CIL exécutent implicites   contrôles d'exécution qui assurent la mémoire et   sécurité de type. À l'origine, la CLI   garantis que les exceptions étaient   précis , ce qui signifie que l'état du programme   a été préservée quand une exception est   jeté. Toutefois, l'application précise   exceptions pour les contrôles implicites marques   certaines optimisations importantes   pratiquement impossible à appliquer.   Les programmeurs peuvent maintenant déclarer, via un   attribut personnalisé, qu'une méthode est   « Détendu », qui dit que les exceptions   résultant de contrôles d'exécution implicites   n'a pas besoin d'être précis.

     

Relaxed contrôles   préserver la vérifiabilité (en préservant   la mémoire et la sécurité de type), tandis que   permettant des optimisations qui Réorganiser   instructions. En particulier,   permet aux optimisations suivantes:

     
      
  • Hisser contrôles d'exécution implicite sur   des boucles.
  •   
  • itérations de boucle Réorganiser   (Par exemple, la vectorisation automatique et   multithreading)
  •   
  • boucles Interchangeabilité
  •   
  • Inlining qui fait un inline   Procédé moins aussi rapide que le   macro équivalent
  •   

Utilisez une version de débogage et vous verrez la différence plus clairement. Avec une version de débogage la première exécution affichera l'emplacement de la ligne de throw ex et le second comme provenant de l'appel réel à BadGuy. Il est évident que le « problème » est l'appel à BadGuy -. Pas la ligne ex throw et vous chasser moins de fantômes avec l'instruction throw; directe

Dans une pile trace ce faible profondeur les avantages ne sont pas aussi évidentes immediatley, dans une pile très profonde vous masque la source réelle du problème et perdre une certaine fidélité en lançant manuellement exception au lieu d'utiliser le construit en re-jeter déclaration.

Sur une note de côté, j'ai trouvé un hack posté sur un blog une fois (je l'ai depuis perdu la référence) qui vous permet de conserver l'appel pile sur rethrow. Cela est surtout utile si vous attrapez une exception dans un contexte (par exemple, dans un thread en cours d'exécution d'une opération asynchrone) et que vous voulez qu'il réémettre dans un autre (par exemple, dans l'autre thread qui a lancé l'opération asynchrone). Il utilise certaines fonctionnalités non documentées inclus pour permettre la conservation des traces de la pile à travers les frontières Remoting.

    //This terrible hack makes sure track trace is preserved if exception is re-thrown
    internal static Exception AppendStackTrace(Exception ex)
    {
        //Fool CLR into appending stack trace information when the exception is re-thrown
        var remoteStackTraceString = typeof(Exception).GetField("_remoteStackTraceString",
                                                                 BindingFlags.Instance |
                                                                 BindingFlags.NonPublic);
        if (remoteStackTraceString != null)
            remoteStackTraceString.SetValue(ex, ex.StackTrace + Environment.NewLine);

        return ex;
    }
scroll top