我有一个问题,源于我的伙伴以不同于我的方式做事。
这样做更好:
try
{
...
}
catch (Exception ex)
{
...
throw;
}
或这个:
try
{
...
}
catch (Exception ex)
{
...
throw ex;
}
他们做同样的事情吗? 这个比那个好吗?
#1楼
我知道这是一个老问题,但我会回答它,因为我不得不在这里不同意所有的答案。
现在,我同意大多数时候你要么做一个简单的throw
,尽可能多地保存关于出错的信息,或者你想抛出一个新的异常,可能包含它作为内部异常,或不,取决于您想要了解导致它的内部事件的可能性。
但有一个例外。 在某些情况下,方法将调用另一个方法,并且在内部调用中导致异常的条件应被视为外部调用上的相同异常。
一个示例是使用另一个集合实现的专用集合。 假设它是一个包含List<T>
但是拒绝重复项的DistinctList<T>
。
如果有人在您的集合类上调用ICollection<T>.CopyTo
,它可能只是在内部集合上直接调用CopyTo
(例如,所有自定义逻辑仅应用于添加到集合或设置它)。 现在,该调用将抛出的条件与您的集合应该抛出的条件完全相同,以匹配ICollection<T>.CopyTo
的文档。
现在,你可能根本就没有抓住这个执行,让它通过。 这里虽然用户在调用DistinctList<T>
上的内容时从List<T>
获得异常。 不是世界末日,但您可能希望隐藏这些实现细节。
或者您可以自己检查:
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;
或者您想构建自己的异常,以便从相关方法的角度更直接地匹配问题。 像上面这样的情况,像这样的重新投掷更有意义,还有很多其他情况。 举一个非常不同的例子,如果使用FileStream
和XmlTextReader
实现的ATOM文件读取器收到文件错误或无效的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;
}
}
#2楼
您应该始终使用以下语法来重新抛出异常,否则您将踩踏堆栈跟踪:
throw;
如果您打印“throw ex”产生的跟踪,您将看到它在该语句上结束,而不是在异常的真实来源。
基本上,使用“throw ex”应被视为刑事犯罪。
#3楼
第一个保留异常的原始堆栈跟踪,第二个跟踪当前位置。
因此第一个是BY FAR越好。
#4楼
第一个更好。 如果您尝试调试第二个并查看调用堆栈,您将看不到原始异常的来源。 如果你真的需要重新抛出,有一些技巧可以保持调用堆栈的完整性(尝试搜索,之前已经回答)。
#5楼
我的偏好是使用
try
{
}
catch (Exception ex)
{
...
throw new Exception ("Put more context here", ex)
}
这样可以保留原始错误,但允许您放置更多上下文,例如对象ID,连接字符串等。 我的异常报告工具通常会有5个链式异常报告,每个报告更多细节。
#6楼
如果抛出异常,并出一个变量(第二个例子)的堆栈跟踪将包括抛出异常的原始方法。
在第一个示例中,将更改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
}
#7楼
你应该总是使用“扔”; 重新抛出.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”覆盖堆栈跟踪的原因。
#8楼
这取决于。 在调试版本中,我希望尽可能少地查看原始堆栈跟踪。 在那种情况下,“扔;” 符合条件。 但是,在发布版本中,(a)我想记录包含原始堆栈跟踪的错误,并且一旦完成,(b)重新设计错误处理以使用户更有意义。 这里的“抛出异常”是有道理的。 确实,重新抛出错误会丢弃原始堆栈跟踪,但是非开发人员无法看到堆栈跟踪信息,因此可以重新抛出错误。
void TrySuspectMethod()
{
try
{
SuspectMethod();
}
#if DEBUG
catch
{
//Don't log error, let developer see
//original stack trace easily
throw;
#else
catch (Exception ex)
{
//Log error for developers and then
//throw a error with a user-oriented message
throw new Exception(String.Format
("Dear user, sorry but: {0}", ex.Message));
#endif
}
}
问题措辞的方式,点击“投掷:”与“投掷前”; 让它有点像红鲱鱼。 真正的选择是在“投掷”之间 和“抛出异常”,“扔前”; 是一个不太可能的特殊情况“抛出异常”。
#9楼
我发现如果在捕获它的同一方法中抛出异常,则不保留堆栈跟踪,因为它的价值。
void testExceptionHandling()
{
try
{
throw new ArithmeticException("illegal expression");
}
catch (Exception ex)
{
throw;
}
finally
{
System.Diagnostics.Debug.WriteLine("finally called.");
}
}