为什么“扔”和“扔前”在这种情况下有相同的行为?

Ste*_*n G 5 .net c# exception rethrow

我惊呆了。我一直认为throw在 catch 块中本身会抛出手头的异常而不改变堆栈跟踪,但throw ex在 catch 块中会更改堆栈跟踪以显示源自语句位置的异常。

采取以下两个代码块。我希望输出会略有不同,因为一个使用throw而另一个使用throw ex,但两者之间的输出是相同的,并且在两种情况下引发初始异常的实际源代码行都丢失了,这对我来说似乎很糟糕。我缺少什么?

第一个示例的行为符合我的预期:

using System;
                    
public class Program
{
    public static void Main()
    {
        try
        {
            DummyWork();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }
        
    private static void DummyWork()
    {
        try
        {
            throw new Exception("dummy");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            throw ex;  // I would expect to lose the information about the inciting line 5 above this one in this case.... and I do.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

第二个示例的行为与第一个示例相同,但我不期望:

using System;
                    
public class Program
{
    public static void Main()
    {
        try
        {
            DummyWork();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }
        
    private static void DummyWork()
    {
        try
        {
            throw new Exception("dummy");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            throw;  // I would NOT expect to lose the information about the inciting line 5 above this one in this case.... But I do.  Output is identical.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:一些评论者说他们无法重现这个 - 这是我的点小提琴(你必须手动编辑它才能在两个版本之间来回):https: //dotnetfiddle.net/Mj7eK5

更新#2:回答一些要求“相同”输出的评论者。这是第一个示例的输出:

System.Exception: dummy
   at Program.DummyWork() in d:\Windows\Temp\xoyupngb.0.cs:line 21
System.Exception: dummy
   at Program.DummyWork() in d:\Windows\Temp\xoyupngb.0.cs:line 26
   at Program.Main() in d:\Windows\Temp\xoyupngb.0.cs:line 9
Run Code Online (Sandbox Code Playgroud)

这是第二个示例的输出:

System.Exception: dummy
   at Program.DummyWork() in d:\Windows\Temp\jy4xgqrf.0.cs:line 21
System.Exception: dummy
   at Program.DummyWork() in d:\Windows\Temp\jy4xgqrf.0.cs:line 26
   at Program.Main() in d:\Windows\Temp\jy4xgqrf.0.cs:line 9
Run Code Online (Sandbox Code Playgroud)

撇开无关紧要的临时文件差异不谈,在这两种情况下,外部 catch(第二个)都缺少初始抛出的第 21 行。我希望在第一个示例中出现这种情况throw ex,但在第二个示例中则不然throw

Ahm*_*eed 7

注意:此答案适用于 .NET Framework。如果您使用 .NET Core 或 .NET 5.0 及更高版本,您可能会观察到不同的行为,如评论中所述。我没有在所有版本的 .NET Core 上进行测试


好吧,让我来尝试一下。throw和之间的区别throw ex已经在“扔”和“扔前”之间有区别吗?中进行了解释。但我会尝试用更清晰的术语来表达,以适应这个问题的叙述。

  • throw ex:从此时重新抛出异常并重置堆栈跟踪。

  • throw:从此时重新抛出异常并保留堆栈跟踪。

让我们看看有问题的代码:

private static void DummyWork()
{
    try
    {
        throw new Exception("dummy");  // Line 21
    }
    catch (Exception ex)
    {
        throw;  // Line 25
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,无论我们使用throwthrow ex,堆栈跟踪始终是:

at Program.DummyWork() in ...:line 25
at Program.Main() in ...:line 9
Run Code Online (Sandbox Code Playgroud)

问:为什么是“25 号线”?

答:因为两者throw从那时起throw ex重新抛出异常。

问:为什么在这种情况下没有区别?

答:因为堆栈跟踪中没有更多堆栈帧需要重置。

问:我们如何看出差异?

好吧,让我们添加另一个级别来生成另一个堆栈帧。代码会是这样的:

private static void DummyWork()
{
    try
    {
        MoreDummyWork();  // Line 21
    }
    catch (Exception ex)
    {
        throw;  //Line 25
    }
}

private static void MoreDummyWork() 
{
    throw new Exception("dummy");  // Line 31
}
Run Code Online (Sandbox Code Playgroud)

在这里,我们可以清楚地看到差异。如果我们使用throw,堆栈跟踪如下:

at Program.MoreDummyWork() in ...:line 31
at Program.DummyWork() in ...:line 25
at Program.Main() in ...:line 9
Run Code Online (Sandbox Code Playgroud)

但如果我们使用throw ex,堆栈跟踪将变为:

at Program.DummyWork() in ...:line 25
at Program.Main() in ...:line 9
Run Code Online (Sandbox Code Playgroud)

问:好的,你说从那时起两者都会抛出异常。如果我想保留原来的行号怎么办?

答:在这种情况下,您可以按照ExceptionDispatchInfo.Capture(ex).Throw();如何在 C# 中重新抛出 InnerException 而不丢失堆栈跟踪?