Why catch and rethrow an exception in C#?

259,148

Solution 1

First; the way that the code in the article does it is evil. throw ex will reset the call stack in the exception to the point where this throw statement is; losing the information about where the exception actually was created.

Second, if you just catch and re-throw like that, I see no added value, the code example above would be just as good (or, given the throw ex bit, even better) without the try-catch.

However, there are cases where you might want to catch and rethrow an exception. Logging could be one of them:

try 
{
    // code that may throw exceptions    
}
catch(Exception ex) 
{
    // add error logging here
    throw;
}

Solution 2

Don't do this,

try 
{
...
}
catch(Exception ex)
{
   throw ex;
}

You'll lose the stack trace information...

Either do,

try { ... }
catch { throw; }

OR

try { ... }
catch (Exception ex)
{
    throw new Exception("My Custom Error Message", ex);
}

One of the reason you might want to rethrow is if you're handling different exceptions, for e.g.

try
{
   ...
}
catch(SQLException sex)
{
   //Do Custom Logging 
   //Don't throw exception - swallow it here
}
catch(OtherException oex)
{
   //Do something else
   throw new WrappedException("Other Exception occured");
}
catch
{
   System.Diagnostics.Debug.WriteLine("Eeep! an error, not to worry, will be handled higher up the call stack");
   throw; //Chuck everything else back up the stack
}

Solution 3

C# (before C# 6) doesn't support CIL "filtered exceptions", which VB does, so in C# 1-5 one reason for re-throwing an exception is that you don't have enough information at the time of catch() to determine whether you wanted to actually catch the exception.

For example, in VB you can do

Try
 ..
Catch Ex As MyException When Ex.ErrorCode = 123
 .. 
End Try

...which would not handle MyExceptions with different ErrorCode values. In C# prior to v6, you would have to catch and re-throw the MyException if the ErrorCode was not 123:

try 
{
   ...
}
catch(MyException ex)
{
    if (ex.ErrorCode != 123) throw;
    ...
}

Since C# 6.0 you can filter just like with VB:

try 
{
  // Do stuff
} 
catch (Exception e) when (e.ErrorCode == 123456) // filter
{
  // Handle, other exceptions will be left alone and bubble up
}

Solution 4

A valid reason for rethrowing exceptions can be that you want to add information to the exception, or perhaps wrap the original exception in one of your own making:

public static string SerializeDTO(DTO dto) {
  try {
      XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
      StringWriter sWriter = new StringWriter();
      xmlSer.Serialize(sWriter, dto);
      return sWriter.ToString();
  }
  catch(Exception ex) {
    string message = 
      String.Format("Something went wrong serializing DTO {0}", DTO);
    throw new MyLibraryException(message, ex);
  }
}

Solution 5

Isn't this exactly equivalent to not handling exceptions at all?

Not exactly, it isn't the same. It resets the exception's stacktrace. Though I agree that this probably is a mistake, and thus an example of bad code.

Share:
259,148
corlettk
Author by

corlettk

I'm not a bad hacker... A red hat of course.

Updated on July 24, 2022

Comments

  • corlettk
    corlettk almost 2 years

    I'm looking at the article C# - Data Transfer Object on serializable DTOs.

    The article includes this piece of code:

    public static string SerializeDTO(DTO dto) {
        try {
            XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
            StringWriter sWriter = new StringWriter();
            xmlSer.Serialize(sWriter, dto);
            return sWriter.ToString();
        }
        catch(Exception ex) {
            throw ex;
        }
    }
    

    The rest of the article looks sane and reasonable (to a noob), but that try-catch-throw throws a WtfException... Isn't this exactly equivalent to not handling exceptions at all?

    Ergo:

    public static string SerializeDTO(DTO dto) {
        XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
        StringWriter sWriter = new StringWriter();
        xmlSer.Serialize(sWriter, dto);
        return sWriter.ToString();
    }
    

    Or am I missing something fundamental about error handling in C#? It's pretty much the same as Java (minus checked exceptions), isn't it? ... That is, they both refined C++.

    The Stack Overflow question The difference between re-throwing parameter-less catch and not doing anything? seems to support my contention that try-catch-throw is-a no-op.


    EDIT:

    Just to summarise for anyone who finds this thread in future...

    DO NOT

    try {
        // Do stuff that might throw an exception
    }
    catch (Exception e) {
        throw e; // This destroys the strack trace information!
    }
    

    The stack trace information can be crucial to identifying the root cause of the problem!

    DO

    try {
        // Do stuff that might throw an exception
    }
    catch (SqlException e) {
        // Log it
        if (e.ErrorCode != NO_ROW_ERROR) { // filter out NoDataFound.
            // Do special cleanup, like maybe closing the "dirty" database connection.
            throw; // This preserves the stack trace
        }
    }
    catch (IOException e) {
        // Log it
        throw;
    }
    catch (Exception e) {
        // Log it
        throw new DAOException("Excrement occurred", e); // wrapped & chained exceptions (just like java).
    }
    finally {
        // Normal clean goes here (like closing open files).
    }
    

    Catch the more specific exceptions before the less specific ones (just like Java).


    References: