关于C#:在.NET中,尝试/捕获(trick/catch)是否有任何优势,因为catch只会再次出现

In .NET, is there any advantage to a try/catch where the catch just rethrows

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
Why catch and rethrow Exception in C#?

我有时会遇到这样的C代码:

1
2
3
4
5
6
7
8
        try
        {
            // Some stuff
        }
        catch (Exception e)
        {
            throw e;
        }

我理解可以做一些类似于记录异常消息然后重新显示它的事情。我说的是一个只会重新引发异常的捕获。我看不出有什么意义。我有三个问题:

1)这有什么好处吗

2)这对代码有什么影响吗?

3)如果catch块如下,会有什么不同吗?

1
2
3
4
        catch (Exception)
        {
            throw;
        }


这将重新引发完全相同的异常:

1
2
3
4
    catch (Exception)
    {
        throw;
    }

然而,这会在没有原始堆栈跟踪的情况下重新引发异常:

1
2
3
4
    catch (Exception e)
    {
        throw e;
    }

对于throw;,通常有一个很好的理由,因为您可以在重新引发异常之前记录异常或执行其他操作。我不知道使用throw e;的任何好理由,因为您将清除宝贵的堆栈跟踪信息。


如果你在抓捕中什么都不做…但这通常用于在重新执行捕获之前执行捕获中的其他操作,如日志记录或其他类型的异常处理。


有什么优势吗

一般来说,不是。所有这些模式都将把堆栈跟踪重置到新的抛出点。这只会让开发人员更难找到问题的根源。

它会减慢代码的速度吗

完全?可能。通过任何可测量的差异降低速度?不。

如果catch块如下,会有什么不同吗?

是的,这种捕获基本上是完全多余的。它将重新引发异常,该异常将维护原始堆栈跟踪,并且对应用程序没有可察觉的影响。


主要区别在于,异常的堆栈跟踪将被更改,以显示它源自第一个示例中try catch的位置。

第二个示例维护堆栈跟踪。


我使用这种技术,以便在调试时在抛出上放置一个断点。有时候我做完后就把它取下来…


我写了一个快速测试来显示不同之处。测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
try
{
    var broken = int.Parse("null");
}
catch (Exception ex1)
{
    System.Diagnostics.Trace.WriteLine(ex1.ToString());
}

try
{
    try
    {
        var broken = int.Parse("null");
    }
    catch (Exception)
    {
        throw;
    }
}
catch (Exception ex2)
{
    System.Diagnostics.Trace.WriteLine(ex2.ToString());
}

try
{
    try
    {
        var broken = int.Parse("null");
    }
    catch (Exception ex3)
    {
        throw ex3;
    }
}
catch (Exception ex4)
{
    System.Diagnostics.Trace.WriteLine(ex4.ToString());
}

运行这个,我得到以下输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
A first chance exception of type 'System.FormatException' occurred in mscorlib.dll
System.FormatException: Input string was not in a correct format.
   at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
   at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
   at System.Int32.Parse(String s)
   at QuickTests.Program.Main(String[] args) in C:\Projects\Test\QuickTests\Program.cs:line 18
A first chance exception of type 'System.FormatException' occurred in mscorlib.dll
A first chance exception of type 'System.FormatException' occurred in QuickTests.exe
System.FormatException: Input string was not in a correct format.
   at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
   at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
   at System.Int32.Parse(String s)
   at QuickTests.Program.Main(String[] args) in C:\Projects\Test\QuickTests\Program.cs:line 33
A first chance exception of type 'System.FormatException' occurred in mscorlib.dll
A first chance exception of type 'System.FormatException' occurred in QuickTests.exe
System.FormatException: Input string was not in a correct format.
   at QuickTests.Program.Main(String[] args) in C:\Projects\Test\QuickTests\Program.cs:line 49

您会注意到前两个异常的工作方式相同。所以,"throw,"不会改变任何东西,直到异常向上移动堆栈为止。但是,"throw ex3;"会导致报告的异常不同,从而更改异常的堆栈跟踪。


它通常适合伐木。另外,如果在re-throw中去掉参数,那么它不会改变e的堆栈跟踪。

有时,您希望允许通过某些类型进行处理,例如,这里对除fooexception之外的所有内容进行特殊处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
try
{
    // ...
}
catch (FooException)
{
    throw;
}
catch (Exception ex)
{
    // handle differently, like wrap with a FooException
    throw new FooException("I pitty the Foo.", ex);
}

如果你真的什么都不做,我只发现一个好处:你可以在throw行上放置一个断点。它使它非常具体(而不仅仅是在抛出异常类型时中断)。

不过,我只在调试时这样做,然后恢复代码。


我看不出有什么优势。如果您没有处理异常,请关闭Try/Catch。这个例子的另一个问题是,您不会抛出实际的异常,而是抛出一个新的异常。

2-是的-但是除非这是一个重复代码的大循环,否则你可能不会注意到有什么不同。

3—是的。在第一个例子中,您正在处理您的调用堆栈。这个例子通过使异常冒泡而不是抛出一个新的异常来保持堆栈的完整性。


就像那样,不,但是,你可能想这样做:

1
2
3
4
5
catch (Exception ex)
{
     LogException(ex);
     throw;
}

其中logException()是一个自定义类,用于erm记录异常或通过电子邮件发送警报等。


当然。

通常,您希望在抛出异常之前记录它,并可能从该方法中记录一些变量值。

不过,仅仅是抓住它扔出去,并不能给你带来很多好处。


我认为重点是确保只抛出一种类型的异常。这是一个很糟糕的反模式imho

例如

1
2
3
4
5
6
7
8
try
{
    throw XYZ();
}
catch(Exception e)
{
    throw e;
}