簡體   English   中英

何時在 c# 中重新拋出異常?

[英]When Rethrow a Exception in c#?

我已經閱讀了為什么我們需要拋出異常並重新拋出它。 但是我對何時重新拋出異常感到困惑? 我在 CalculationOperationNotSupportedException catch 中放入 throw 時添加了一個示例,之后,我將 Stack Trace 與 Rethrowing 和不 Rethrowing 進行了比較。 99% 都是一樣的,但是當您重新拋出異常時,它只會添加位置。 當然,如果你准確地進行了兩次堆棧跟蹤。 第 35 行是“拋出”位置號,第 28 行是 int result =calculator.Calculate(number1, number2, operation); 我認為不在這里重新拋出的堆棧跟蹤更好。 你怎么看?

Stack Trace without rethrowing(throw) 我評論了它。

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

在 catch 中重新拋出的堆棧跟蹤(CalculationOperationNotSupportedException ex)

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

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

有兩篇關於我經常鏈接的主題的文章。 我認為它們是必讀的。

基本上,如果您無法處理異常,則不應捕獲異常。 但有時,您必須對任何有關 Exception 的規則進行例外處理(沒有雙關語意)。 您可能需要更廣泛地捕捉,然后將“捕捉和釋放”應用於您獲得的額外異常。 例如,這是我嘗試復制 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;
}

不,您對投擲和再投擲的工作原理的理解是錯誤的。

當你拋出一個異常時,你會丟失在新拋出的異常之前發生的所有堆棧跟蹤信息。 問題是您將所有代碼都放在同一個文件中,並且在讀取和正確比較每個堆棧跟蹤中的信息時遇到了麻煩。

構建以下代碼,確保每個類都在不同的 .cs 文件中。 運行它並比較兩個打印的堆棧跟蹤,您將看到 throwing 丟失了信息:

//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();
    }
}

這個程序的輸出是,在我的電腦中:

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

如您所見,當您拋出時, Foo拋出的原始異常的所有信息都丟失了。

我適用的一般規則是:

  1. 如果你不能處理它,不要抓住它
  2. 如果您無法處理它,但您需要捕獲它以進行清理、記錄信息或其他任何事情,請重新拋出它。
  3. 如果您無法處理它,但您不能讓異常中的敏感或不適當的信息到達消費者,則拋出一個具有足夠信息的新信息,以便稍后進行正確的調試。
  4. 如果你能處理它,那就處理它。

99% 的情況下,我會應用規則 #1。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM