GOTCHA 15 rethrow isn t consistent


GOTCHA #15 rethrow isn't consistent

In some situations, you may need to continue propagating an exception up the call stack. For instance, you may feel that you have not handled it successfully, or perhaps you just want to log the exception. In these cases, you can throw the exception again. If e is a reference to an exception object, the call that comes to mind is tHRow e. But what is the consequence of this statement? This is a good example of things getting lost in the translation to MSIL. Consider Example 2-9.

Example 2-9. Behavior of a throw statement

C# (rethrow)

 using System; namespace ThrowingException {     class Test     {         public static void Method1()         {             throw new ApplicationException();         }         public static void Method2()         {             try             {                 Method1();             }              catch(Exception ex)             {                 // Code to log may go here.                  throw ex;             }         }         public static void Method3()         {             try             {                 Method1();             }              catch(Exception)             {                 // Code to log may go here.                  throw;             }         }         [STAThread]         static void Main(string[] args)         {             try             {                 Console.WriteLine("----- Calling Method2");                 Method2();             }             catch(Exception ex)             {                  Console.WriteLine(ex);             }             try             {                 Console.WriteLine("----- Calling Method3");                 Method3();             }             catch(Exception ex)             {                  Console.WriteLine(ex);             }         }     } } 

VB.NET (rethrow)

 Module Test     Public Sub Method1()         Throw New ApplicationException     End Sub     Public Sub Method2()         Try             Method1()          Catch ex As Exception             'code to log may go here              Throw ex         End Try     End Sub     Public Sub Method3()         Try             Method1()          Catch ex As Exception             'code to log may go here              Throw         End Try     End Sub     Public Sub Main()         Try             Console.WriteLine("----- Calling Method2")             Method2()         Catch ex As Exception              Console.WriteLine(ex)         End Try         Try             Console.WriteLine("----- Calling Method3")             Method3()         Catch ex As Exception              Console.WriteLine(ex)         End Try     End Sub End Module 

In Example 2-9, Method2() uses throw ex to propagate the exception it caught. Method3(), on the other hand, uses only tHRow. The output from the above program is shown in Figure 2-8.

Note that when the exception is caught in Main() from Method2() (the one that uses throw ex), the stack trace does not indicate that Method1() was the origin of the exception. However, when the exception is caught in Main() from Method3() (the one that uses throw without the ex), the stack trace points all the way to Method1(), where the exception originated.

Figure 2-8. Output from Example 2-9


What is the reason for this difference? It becomes clear if you use ildasm.exe to examine the assembly for Method2() and Method3(), shown in Example 2-10 as generated from the C# code.

Example 2-10. MSIL with difference between throw ex and throw
 .method public hidebysig static void  Method2() cil managed {   // Code size       11 (0xb)   .maxstack  1    .locals init ([0] class [mscorlib]System.Exception ex)   .try   {     IL_0000:  call       void ThrowingException.Test::Method1()     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 Test::Method2 .method public hidebysig static void  Method3() cil managed {   // Code size       11 (0xb)   .maxstack  1   .try   {     IL_0000:  call       void ThrowingException.Test::Method1()     IL_0005:  leave.s    IL_000a   }  // end .try   catch [mscorlib]System.Exception   {      IL_0007:  pop     IL_0008:  rethrow   }  // end handler   IL_000a:  ret } // end of method Test::Method3 

As you can see from the MSIL, throw ex TRanslates to a fresh throw on the call stack. However, throw by itself translates to a rethrow statement at the MSIL level. The latter results in the true rethrow of the original exception, whereas the former is treated as a new exception. So you would want to use throw instead of tHRow ex.

Another option is to use the InnerException property of the Exception class to propagate the exception details. In the catch block for Method2() you can create an exception and set the exception you caught as its constructor argument, as in the following C# code segment:

             catch(Exception ex)             {                 // Code to log may go here.                 //throw ex;                 throw new ApplicationException(                         "Exception logged", ex);             } 

The VB.NET version is:

         Catch ex As Exception             'code to log may go here             'Throw ex             Throw New ApplicationException("Exception logged", ex)         End Try 

As a result of this change you will see the output shown in Figure 2-9.

Figure 2-9. Output after the change to use InnerException


The catch blocks in Main() call Console.WriteLine(ex). This in turn calls the ToString() method on the Exception, which prints the details of that exception instance. If the Exception contains a non-null InnerException, the ToString() method of Exception will also print the details of the inner exception. You can see this in the output between the "--->" (in the second line from the top of Figure 2-9) and "--- End of inner exception stack trace ---."

IN A NUTSHELL

Use throw instead of throw ex to propagate an exception. Alternately, use InnerException.

SEE ALSO

Gotcha #26, "Details of exception may be hidden."



    .NET Gotachas
    .NET Gotachas
    ISBN: N/A
    EAN: N/A
    Year: 2005
    Pages: 126

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net