Domanda

riferendosi a un sacco di documentazione in rete, in particolare su SO, ad esempio: Qual è il modo corretto di ri-generare un'eccezione in C #? ci dovrebbe essere una differenza tra "tiro e;" e "buttare;".

Ma, da: http://bartdesmet.net /blogs/bart/archive/2006/03/12/3815.aspx ,

questo codice:

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;
  }
   }
}

dà il seguente risultato:

$ /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()

che è in totale contrasto con il post sul blog.

Lo stesso tipo di risultato si ottiene con il codice: http://crazorsharp.blogspot.com/2009/08/rethrowing-exception-without-resetting.html

domanda iniziale : che cosa sto facendo di sbagliato

Aggiorna : stesso risultato con .Net 3.5 / csc.exe 3.5.30729.4926

SUMUP :. Tutte le vostre risposte sono stati grandi, grazie ancora

Quindi la ragione è effettivamente inlining causa delle oscillazioni a 64 bit.

Ho dovuto scegliere una sola risposta, e qui è il motivo per cui ho scelto LukeH Risposta:

  • ha indovinato il problema inlining e il fatto che può essere correlato alla mia architettura a 64 bit,

  • ha fornito la bandiera NoInlining che è il modo più semplice per evitare questo comportamento.

Tuttavia, questo problema ora sorge un'altra domanda: questo comportamento compatibile con tutte le specifiche .Net: quelli CLR e quelli linguaggio di programmazione C #

Aggiorna : questa ottimizzazione sembra secondo conforme a: Gettare VS rethrow: stesso risultato (grazie 0xA3 )

Grazie in anticipo per il vostro aiuto.

È stato utile?

Soluzione

Non riesco a replicare il problema -. Usando .NET 3.5 (32-bit) mi dà gli stessi risultati descritti nell'articolo di Bart

La mia ipotesi è che il .NET 4 compilatore / jitter - o forse è il 64-bit compilatore / jitter se questo sta accadendo sotto 3,5 troppo - è il metodo inlining BadGuy sui metodi di chiamata. Prova ad aggiungere il seguente MethodImpl attributo a BadGuy e vedere se questo fa alcuna differenza:

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

Altri suggerimenti

Ho provato a fare funzionare questo codice me stesso e le opere di debug come mi aspettavo, ma ho ottenuto lo stesso risultato nella build di rilascio.

Ho il sospetto che quello che sta succedendo è che il compilatore inline ha semplicemente sostituito la chiamata ImbrattatatoreSiti () con lancio new Exception(); perché questa è l'unica affermazione in ImbrattatatoreSiti ().

Se si spegne l'opzione 'codice Ottimizzare' nelle proprietà del progetto -.> Costruzione dello schermo, quindi sia di rilascio e di debug produce lo stesso risultato che mostra Badguy () nella parte superiore della traccia dello stack

Sembra che gli ottimizzatori di JIT fa un certo lavoro qui. Come si può vedere, lo stack di chiamate nel secondo caso è diverso da quello nel primo caso quando si esegue la build di debug. Tuttavia, nella build di rilascio, entrambi stack di chiamate sono identiche a causa di ottimizzazione.

Per vedere che questo è legato al jitter è possibile decorare i metodi con MethodImplAttribute attributo:

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

Si noti che l'IL è ancora diverso per ThrowWithoutVariable e 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

Aggiorna per rispondere alla tua domanda di follow-up se questo è conforme alla specifica CLI

Infatti conforme, cioè per consentire il compilatore JIT per abilitare ottimizzazioni importanti. allegato F stati a pagina 52 (enfasi da me):

  

Alcune istruzioni CIL eseguono implicita   controlli in fase di esecuzione che assicurano la memoria e   digitare sicurezza. In origine, il CLI   garantiti che le eccezioni erano   preciso , il che significa che lo stato del programma   è stata preservata quando un'eccezione era   gettato. Tuttavia, far rispettare precisi   eccezioni per assegni implicite marche   alcune ottimizzazioni importanti   praticamente impossibile da applicare.   I programmatori possono ora dichiarare, tramite un   attributo personalizzato, che è un metodo   “Rilassato”, che dice che le eccezioni   derivanti dai controlli in fase di esecuzione implicite   non deve essere preciso.

     

controlli Relaxed   preservare verificabilità (preservando   memoria e sicurezza tipo) mentre   ottimizzazioni permettendo che riordino   Istruzioni. In particolare, essa   permette le seguenti ottimizzazioni:

     
      
  • sollevamento implicita run-time out controlli   di loop.
  •   
  • iterazioni del ciclo Riordinamento   (Ad esempio, vettorizzazione e automatico   multithreading)
  •   
  • Passaggio da loop
  •   
  • Inlining che fa un inline   Procedimento almeno veloce come il   macro equivalente
  •   

Utilizzare una build di debug e vedrete la differenza in modo più chiaro. Con una build di debug la prima esecuzione mostrerà la posizione come la linea throw ex e la seconda come proveniente dalla chiamata effettiva al BadGuy. Ovviamente il 'problema' è la chiamata a ImbrattatatoreSiti -. Non il tiro dell'ex linea e ti inseguono un minor numero di fantasmi con la dichiarazione throw; diretta

In una pila tracciare questa superficiale i benefici non sono così dispensati evidenti, in modo molto profondo si impilare ti mascherare la vera fonte del problema e perdere un po 'la fedeltà lanciando manualmente eccezione invece di utilizzare il costruito nel ri-lancio comunicato.

Una nota a parte, ho trovato un hack pubblicato su un blog, una volta (Da allora ho perso il riferimento), che permette di mantenere il call-stack su rethrow. Questo è particolarmente utile se si cattura un'eccezione in un contesto (ad esempio, in un thread in esecuzione un'operazione asincrona) e si desidera rigenerare in un altro (per esempio, in altro thread che ha lanciato l'operazione asincrona). Si avvale di alcune funzionalità non documentate incluso per permettere la conservazione delle tracce dello stack attraverso servizi remoti confini.

    //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;
    }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top