我有一个问题,源于我的伙伴以不同于我的方式做事.
这样做更好:
try { ... } catch (Exception ex) { ... throw; }
或这个:
try { ... } catch (Exception ex) { ... throw ex; }
他们做同样的事情吗?这个比那个好吗?
您应该始终使用以下语法来重新抛出异常,否则您将踩踏堆栈跟踪:
throw;
如果您打印"throw ex"产生的跟踪,您将看到它在该语句上结束,而不是在异常的真实来源.
基本上,使用"throw ex"应被视为刑事犯罪.
我的偏好是使用
try { } catch (Exception ex) { ... throw new Exception ("Put more context here", ex) }
这样可以保留原始错误,但允许您放置更多上下文,例如对象ID,连接字符串等.我的异常报告工具通常会有5个链式异常报告,每个报告更多细节.
如果抛出异常,并出一个变量(第二个例子)的堆栈跟踪将包括抛出异常的原始方法.
在第一个示例中,将更改StackTrace以反映当前方法.
例:
static string ReadAFile(string fileName) { string result = string.Empty; try { result = File.ReadAllLines(fileName); } catch(Exception ex) { throw ex; // This will show ReadAFile in the StackTrace throw; // This will show ReadAllLines in the StackTrace }
第一个保留异常的原始堆栈跟踪,第二个跟踪当前位置.
因此第一个是BY FAR越好.
我知道这是一个老问题,但我会回答它,因为我不得不在这里不同意所有的答案.
现在,我同意大多数时候你要么做一个简单的事情throw
,尽可能多地保存关于出错的信息,或者你想抛出一个新的异常,可能包含那个作为内部异常,或者不是,取决于你想知道导致它的内部事件的可能性.
但有一个例外.在某些情况下,方法将调用另一个方法,并且在内部调用中导致异常的条件应被视为外部调用上的相同异常.
一个示例是使用另一个集合实现的专用集合.让我们说这是一个DistinctList
包装List
但拒绝重复的项目.
如果某人调用ICollection
了您的集合类,那么它可能只是CopyTo
对内部集合的直接调用(例如,所有自定义逻辑仅适用于添加到集合或设置它).现在,该调用将抛出的条件与您的集合应该抛出的条件完全相同,以匹配文档ICollection
.
现在,你可能根本就没有抓住这个执行,让它通过.虽然用户List
在他们调用某些内容时会收到异常DistinctList
.不是世界末日,但您可能希望隐藏这些实现细节.
或者您可以自己检查:
public CopyTo(T[] array, int arrayIndex) { if(array == null) throw new ArgumentNullException("array"); if(arrayIndex < 0) throw new ArgumentOutOfRangeException("arrayIndex", "Array Index must be zero or greater."); if(Count > array.Length + arrayIndex) throw new ArgumentException("Not enough room in array to copy elements starting at index given."); _innerList.CopyTo(array, arrayIndex); }
这不是更糟糕的代码,因为它是样板文件,我们可能只是从其他实现中复制它,CopyTo
而不是简单的传递,我们必须自己实现它.尽管如此,它还是不必要地重复了将要完成的完全相同的检查_innerList.CopyTo(array, arrayIndex)
,因此它唯一添加到我们的代码中的是6行,其中可能存在错误.
我们可以检查并包装:
public CopyTo(T[] array, int arrayIndex) { try { _innerList.CopyTo(array, arrayIndex); } catch(ArgumentNullException ane) { throw new ArgumentNullException("array", ane); } catch(ArgumentOutOfRangeException aore) { throw new ArgumentOutOfRangeException("Array Index must be zero or greater.", aore); } catch(ArgumentException ae) { throw new ArgumentException("Not enough room in array to copy elements starting at index given.", ae); } }
对于可能存在错误的新代码而言,情况更糟.而且我们没有从内心异常中获得一些东西.如果我们将一个null数组传递给这个方法并接收一个ArgumentNullException
,我们就不会通过检查内部异常并学习调用_innerList.CopyTo
传递一个空数组并抛出一个来学习任何东西ArgumentNullException
.
在这里,我们可以做我们想要的一切:
public CopyTo(T[] array, int arrayIndex) { try { _innerList.CopyTo(array, arrayIndex); } catch(ArgumentException ae) { throw ae; } }
如果用户使用不正确的参数调用它,我们期望必须抛出的每个异常将被重新抛出正确抛出.如果这里使用的逻辑中存在一个错误,那就是两行中的一行 - 要么我们错误地决定这种情况是这种方法有效,要么我们在ArgumentException
查找异常类型时错了.这是catch块可能拥有的唯一两个bug.
现在.我仍然同意,大多数时候你要么想要一个普通的,throw;
要么你想构建自己的异常,以便从问题方法的角度更直接地匹配问题.像上面这样的情况,像这样的重新投掷更有意义,还有很多其他情况.举一个非常不同的例子,如果一个ATOM文件阅读器用a实现FileStream
并且XmlTextReader
接收到文件错误或无效的XML,那么它可能想要抛出从这些类中收到的完全相同的异常,但它应该看向调用者这AtomFileReader
是投掷一个FileNotFoundException
或者XmlException
,所以他们可能是同样重新投掷的候选人.
编辑:
我们也可以将两者结合起来:
public CopyTo(T[] array, int arrayIndex) { try { _innerList.CopyTo(array, arrayIndex); } catch(ArgumentException ae) { throw ae; } catch(Exception ex) { //we weren't expecting this, there must be a bug in our code that put //us into an invalid state, and subsequently let this exception happen. LogException(ex); throw; } }
你应该总是使用"扔"; 重新抛出.NET中的异常,
请参阅此处,http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx
基本上MSIL(CIL)有两条指令 - "throw"和"rethrow"以及C#的"throw ex"; 被编译成MSIL的"throw"和C#的"throw". - 进入MSIL"重新抛出"!基本上我可以看到"throw ex"覆盖堆栈跟踪的原因.
首先是更好。如果尝试调试第二个并查看调用堆栈,您将看不到原始异常的来源。如果确实需要重新抛出,有一些技巧可以使调用栈保持完整(尝试搜索,之前已经有人回答了)。