简体   繁体   中英

When Rethrow a Exception in c#?

I have read about why we need to throw an exception and Rethrow it. But I have confused about when Rethrow an exception? I added an example when I put throw in CalculationOperationNotSupportedException catch, and after that, I compared Stack Trace with Rethrowing and without Rethrowing. It's the same 99%, but when you rethrow an exception, it just adds location. Of course, if you accurately two stack trace. Line 35 is "throw" location number, and line 28 is int result = calculator.Calculate(number1, number2, operation); I think Stack Trace without rethrowing in here is better. What do you think about that?

Stack Trace without rethrowing(throw) I commented it.

at ConsoleCalculator.Calculator.Calculate(Int32 number1, Int32 number2, String operation) in C:\\Users\\Behnam\\Desktop\\c-sharp-error-handling-exceptions\\06\\demos\\after\\03UsingExceptions\\ConsoleCalculator\\Calculator.cs:line 25 at ConsoleCalculator.Program.Main(String[] args) in C:\\Users\\Behnam\\Desktop\\c-sharp-error-handling-exceptions\\06\\demos\\after\\03UsingExceptions\\ConsoleCalculator\\Program.cs:line 28

Stack Trace with rethrow in catch (CalculationOperationNotSupportedException ex)

at ConsoleCalculator.Calculator.Calculate(Int32 number1, Int32 number2, String operation) in C:\\Users\\Behnam\\Desktop\\c-sharp-error-handling-exceptions\\06\\demos\\after\\03UsingExceptions\\ConsoleCalculator\\Calculator.cs:line 25 at ConsoleCalculator.Program.Main(String[] args) in C:\\Users\\Behnam\\Desktop\\c-sharp-error-handling-exceptions\\06\\demos\\after\\03UsingExceptions\\ConsoleCalculator\\Program.cs:line 35

public int Calculate(int number1, int number2, string operation)
{
    string nonNullOperation = 
        operation ?? throw new ArgumentNullException(nameof(operation));

    if (nonNullOperation == "/")
    {
        try
        {
            return Divide(number1, number2);
        }
        catch (ArithmeticException ex)
        {
            throw new CalculationException("An error occurred during division", ex);
        }
    }
    else
    {
        throw new CalculationOperationNotSupportedException(operation);
    }
}

static void Main(string[] args)
{
    var calculator = new Calculator();
    int number1=1;
    int number2=1;
    string operation = "+";
    try
    {
        int result = calculator.Calculate(number1, number2, operation);
        DisplayResult(result);
    }
    catch (CalculationOperationNotSupportedException ex)
    {
        // Log.Error(ex);
        WriteLine(ex);
        throw;
    }
}

There are two articles on the thematic I link often. I consider them required reading.

At the basic, you should not catch an exception if you can not handle it. But sometimes, you have to make exceptions (no pun intended) to any rule about Exception. You might have to catch wider and then apply "catch and release" to the extra Exceptions you got. For example, here is my atempt at repilicating TryParse:

//Parse throws ArgumentNull, Format and Overflow Exceptions.
//And they only have Exception as base class in common, but identical handling code (output = 0 and return false).

bool TryParse(string input, out int output){
  try{
    output = int.Parse(input);
  }
  catch (Exception ex){
    if(ex is ArgumentNullException ||
      ex is FormatException ||
      ex is OverflowException){
      //these are the exceptions I am looking for. I will do my thing.
      output = 0;
      return false;
    }
    else{
      //Not the exceptions I expect. Best to just let them go on their way.
      throw;
    }
  }

  //I am pretty sure the Exception replaces the return value in exception case. 
  //So this one will only be returned without any Exceptions, expected or unexpected
  return true;
}

No, your understanding of how throwing and rethrowing works is incorrect.

When you throw an exception you loose all stack trace information of what happened prior to the newly thrown exception. The problem is that you have all your code in the same files and you are having trouble reading and correctly comparing the information in each stacktrace.

Build the following code, making sure that each class is in a diferent .cs file. Run it and compare both printed stack traces and you will see that throwing loses information:

//Foo.cs
public class Foo
{
    public void Crash() => throw new Exception();
}

//Blah.cs
public class Blah
{
    public void CrashAndThrow()
    {
        var foo = new Foo();

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

    public void CrashAndReThrow()
    {
        var foo = new Foo();

        try 
        { 
            foo.Crash();
        }
        catch
        {
            throw;
        }
    }
}

//Program.cs
class Program
{
    static void Main(string[] args)
    {
        var bla = new Blah();
        try
        {
            bla.CrashAndThrow();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Throw:");
            Console.WriteLine(ex.StackTrace);
            Console.WriteLine();
        }

        try
        {
            bla.CrashAndReThrow();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Rethrow:");
            Console.WriteLine(ex.StackTrace);
        }

        Console.ReadLine();
    }
}

The output of this program is, in my computer:

Throw:
   at SOStuff.Alpha.Blah.CrashAndThrow() in ...\SOStuff\Blah.cs:line 16
   at SOStuff.Program.Main(String[] args) in ...\SOStuff\Program.cs:line 13

Rethrow:
   at SOStuff.Alpha.Foo.Crash() in ...\SOStuff\Foo.cs:line 7
   at SOStuff.Alpha.Blah.CrashAndReThrow() in ...\SOStuff\Blah.cs:line 24
   at SOStuff.Program.Main(String[] args) in ...\SOStuff\Program.cs:line 24

As you can see, when you throw, all information of the orignal exception thrown in Foo is lost.

The general rules I apply are:

  1. If you can't handle it, don't catch it
  2. If you can't handle it, but you need to catch it in order to clean up, log information, or whatever, rethrow it.
  3. If you can't handle it but you can't allow sensistive or non appropiate information in the exception reach the consumer, then throw a new one with enough information that allows correct debugging later on.
  4. If you can handle it, then handle it.

99% of the time, I apply rule #1.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM