了解 throw ex 和 throw 之间的区别,为什么在此示例中保留了原始StackTrace:
static void Main(string[] args)
{
try
{
LongFaultyMethod();
}
catch (System.Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
}
static void LongFaultyMethod()
{
try
{
int x = 20;
SomethingThatThrowsException(x);
}
catch (Exception)
{
throw;
}
}
static void SomethingThatThrowsException(int x)
{
int y = x / (x - x);
}
但不是在这一个:
static void Main(string[] args)
{
try
{
LongFaultyMethod();
}
catch (System.Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
}
static void LongFaultyMethod()
{
try
{
int x = 20;
int y = x / (x - 20);
}
catch (Exception)
{
throw;
}
}
第二种情况是产生与投掷前相同的输出?
在这两种情况下,都希望看到y被初始化的行号。
答案 0 :(得分:17)
我不确定这个限制是否在C#语言,CLI或这些限制的Microsoft实现中,但是您的第二个示例是需要显式调用Exception.InternalPreserveStackTrace
的情况,如以下帖子。由于此方法为internal
,因此通常必须通过反射调用。通过为呼叫创建Action<Exception>
,几乎可以完全缓解此过程中涉及的性能问题,如本答案末尾所示。
参考:Rethrowing exceptions and preserving the full call stack trace
编辑:重新审核ECMA-335分区I§12.4.2(异常处理)和分区III§4.24(重新抛出)后,我现在相信您所看到的行为是语义错误CLR(微软的CLI实现)。对行为的唯一具体引用是“A rethrow
不会更改对象中的堆栈跟踪。”在这里描述的情况下,重新抛出事实上正在改变堆栈跟踪,使PreserveStackTrace
hack成为了解已知CLR缺陷的解决方法。
static void LongFaultyMethod()
{
try
{
int x = 20;
int y = x / (x - 20);
}
catch (Exception ex)
{
PreserveStackTrace(ex); // <-- add this line
throw;
}
}
PreserveStackTrace
这是对该博客条目的优化:
private static readonly Action<Exception> _internalPreserveStackTrace =
(Action<Exception>)Delegate.CreateDelegate(
typeof(Action<Exception>),
typeof(Exception).GetMethod(
"InternalPreserveStackTrace",
BindingFlags.Instance | BindingFlags.NonPublic));
public static void PreserveStackTrace(Exception e)
{
_internalPreserveStackTrace(e);
}
答案 1 :(得分:2)
因为在第二个例子中,你是从同一个方法重新抛出异常。首先,它从不同的方法抛出,这就是为什么。在一个方法范围中,堆栈跟踪只能是一个。
执行以下操作,最好的方法是始终在新异常中包含异常,以便您看到异常深度。
“如果重新抛出已经发布了 相同的方法(异常堆栈跟踪 只有一个行号信息 每种方法,你永远不会看到堆栈跟踪 在方法A中,在第2行 异常被抛出然后同样 方法A,它是从线上重新抛出的 数字17,它只包含最后一个 异常所在的行号 重新抛出“
try
{
int x = 20;
int y = x / (x - 20);
}
catch (Exception ex)
{
// do something here.. like log or something
throw new Exception("Internal Exception", ex);
}
我很惊讶这么多评论都不读我的评论!!我在评论中写道,你应该安全地记录这个,有各种各样的原因,如果顶级代码吃了异常并且你不知道抛出了哪个和哪个异常,日志记录会帮助你交叉异常!! !
如果您不需要记录,则不要捕获异常。