本文包括以下代码:
public static string SerializeDTO(DTO dto) {
try {
XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
StringWriter sWriter = new StringWriter();
xmlSer.Serialize(sWriter, dto);
return sWriter.ToString();
}
catch(Exception ex) {
throw ex;
}
}
本文的其余部分看起来很合理(对菜鸟而言),但是try-catch-throw引发了WtfException ... 这不完全等同于根本不处理异常吗?
Ergo:
public static string SerializeDTO(DTO dto) {
XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
StringWriter sWriter = new StringWriter();
xmlSer.Serialize(sWriter, dto);
return sWriter.ToString();
}
还是我缺少有关C#中错误处理的基本知识? 它与Java(减去检查的异常)几乎相同,不是吗? ...也就是说,他们都精炼了C ++。
堆栈溢出问题重新抛出无参数的捕获和什么都不做之间的区别? 似乎支持我的观点,即try-catch-throw是-no-op。
编辑:
总结一下,以便将来找到该线程的任何人...
不要
try {
// Do stuff that might throw an exception
}
catch (Exception e) {
throw e; // This destroys the strack trace information!
}
堆栈跟踪信息对于确定问题的根本原因可能至关重要!
做
try {
// Do stuff that might throw an exception
}
catch (SqlException e) {
// Log it
if (e.ErrorCode != NO_ROW_ERROR) { // filter out NoDataFound.
// Do special cleanup, like maybe closing the "dirty" database connection.
throw; // This preserves the stack trace
}
}
catch (IOException e) {
// Log it
throw;
}
catch (Exception e) {
// Log it
throw new DAOException("Excrement occurred", e); // wrapped & chained exceptions (just like java).
}
finally {
// Normal clean goes here (like closing open files).
}
在不太具体的异常之前捕获更具体的异常(就像Java)。
参考文献:
#1楼
引发异常的一个可能原因是禁用任何更深层的异常过滤器以免过滤掉( 随机旧链接 )。 但是,当然,如果那是意图,那么那里会有一条评论说。
#2楼
关于场景捕获日志重新抛出的大多数答案。
与其在您的代码中编写它, 不如考虑使用AOP,尤其是使用具有OnExceptionOptions IncludeParameterValue和IncludeThisArgument的Postsharp.Diagnostic.Toolkit 。
#3楼
抱歉,但是许多示例,例如“改进的设计”仍然令人发指,甚至可能极具误导性。 尝试{}捕获{日志; throw}毫无意义。 异常日志记录应在应用程序内部的中央位置进行。 无论如何,异常都会使stacktrace冒泡,为什么不将它们记录到某个位置并靠近系统边界呢?
当将上下文(即给定示例中的DTO)仅序列化到日志消息中时,应谨慎使用。 它可以轻松地包含敏感信息,而这些信息可能不希望触及所有可以访问日志文件的人。 而且,如果您不向异常添加任何新信息,我真的看不到异常包装的意义。 好的旧Java有一定的意义,它要求调用者知道在调用代码之前应该期望什么样的异常。 由于您在.NET中没有此功能,因此在至少80%的情况下,包装都无济于事。
#4楼
我拥有类似代码的主要原因:
try
{
//Some code
}
catch (Exception e)
{
throw;
}
这样我就可以在catch中有一个断点,该断点具有实例化的异常对象。 在开发/调试时,我经常这样做。 当然,编译器会向我发出所有未使用的e的警告,理想情况下,应在发布版本之前将其删除。
它们在调试期间很好。
#5楼
除了其他人所说的以外,请参阅我对一个相关问题的回答 ,该问题表明捕获和重新抛出不是禁止操作(在VB中,但是某些代码可以从VB调用C#)。
#6楼
人们没有提到的一点是,虽然.NET语言并没有真正区分,但是在发生异常时是否应该采取措施以及是否可以解决这一问题实际上是不同的问题。 在很多情况下,一个人应该基于异常采取行动,一个人没有希望解决的希望,并且在某些情况下,“解决”异常所需要的所有事情就是将堆栈展开到某个特定点,而无需采取进一步的措施。
由于人们通常只应该“抓住”一个人可以“处理”的事情,因此,许多代码在异常发生时应该采取的措施就没有了。 例如,许多代码将获取一个锁,将受保护对象“临时”置于违反其不变式的状态,然后将其置于合法状态,然后在其他人看不到该对象之前释放该锁。 如果在对象处于危险无效状态时发生异常,通常的做法是在对象仍处于该状态的情况下释放锁。 更好的模式是在对象处于“危险”状态时发生异常,该异常会明显使锁无效,因此将来任何尝试获取该锁的操作都会立即失败。 始终如一地使用这种模式将大大提高所谓的“ Pokemon”异常处理的安全性,恕我直言,IMHO的声誉不好,主要是因为该代码允许异常渗入,而无需先采取适当的措施。
在大多数.NET语言中,代码根据异常采取行动的唯一方法是catch
异常(即使它知道它不会解决异常),执行有问题的动作然后throw
)。 如果代码不关心抛出什么异常,则另一种可能的方法是在try/finally
块中使用ok
标志。 将ok
标志在该块之前设置为false
,在该块退出之前以及该块内的任何return
之前设置为true
。 然后,在finally
,假设如果未设置ok
,那么肯定发生了异常。 从语义throw
,这种方法比catch
/ throw
更好,但它丑陋且难以维护。
#7楼
虽然许多其他答案都提供了很好的示例,说明了为什么您可能想捕获重新引发的异常,但似乎没有人提到“最终”情况。
这方面的一个示例是,您有一个方法来设置光标(例如,设置为等待光标),该方法有多个退出点(例如,if()return;),并且您想确保在方法的结尾。
为此,您可以将所有代码包装在try / catch / finally中。 在最后设置光标回到右光标。 为了避免隐藏任何有效的异常,请将其重新放入捕获中。
try
{
Cursor.Current = Cursors.WaitCursor;
// Test something
if (testResult) return;
// Do something else
}
catch
{
throw;
}
finally
{
Cursor.Current = Cursors.Default;
}
#8楼
当您的程序针对库或dll起作用时,这可能会很有用。
此重新抛出结构可用于有目的地重置调用堆栈,以便您不必从函数内部的单个函数中看到异常,而可以看到该异常。
我认为这只是为了使抛出的异常更干净,并且不会进入库的“根目录”。
#9楼
您不想抛出ex-因为这会丢失调用堆栈。 请参阅异常处理 (MSDN)。
是的,try ... catch并没有做任何有用的事情(除了丢失调用堆栈-因此实际上更糟-除非出于某些原因您不想公开此信息)。
#10楼
第一; 本文中代码的执行方式是邪恶的。 throw ex
会将异常中的调用堆栈重置到该throw语句所在的位置; 丢失有关实际在何处创建异常的信息。
其次,如果您只是像这样捕获并重新抛出,我看不到有任何附加值,那么上面的代码示例将在没有try-catch的情况下一样好(或者,考虑到throw ex
,甚至更好)。
但是,在某些情况下,您可能想捕获并重新抛出异常。 日志记录可能是其中之一:
try
{
// code that may throw exceptions
}
catch(Exception ex)
{
// add error logging here
throw;
}
#11楼
抛出异常的一个有效原因可能是您想向异常添加信息,或者可能是将原始异常包装成自己的一种:
public static string SerializeDTO(DTO dto) {
try {
XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
StringWriter sWriter = new StringWriter();
xmlSer.Serialize(sWriter, dto);
return sWriter.ToString();
}
catch(Exception ex) {
string message =
String.Format("Something went wrong serializing DTO {0}", DTO);
throw new MyLibraryException(message, ex);
}
}
#12楼
不要这样
try
{
...
}
catch(Exception ex)
{
throw ex;
}
您将丢失堆栈跟踪信息...
要么做,
try { ... }
catch { throw; }
要么
try { ... }
catch (Exception ex)
{
throw new Exception("My Custom Error Message", ex);
}
您可能要重新抛出的原因之一是,如果您正在处理其他异常,例如
try
{
...
}
catch(SQLException sex)
{
//Do Custom Logging
//Don't throw exception - swallow it here
}
catch(OtherException oex)
{
//Do something else
throw new WrappedException("Other Exception occured");
}
catch
{
System.Diagnostics.Debug.WriteLine("Eeep! an error, not to worry, will be handled higher up the call stack");
throw; //Chuck everything else back up the stack
}
#13楼
这取决于您在catch块中的操作以及是否要将错误传递给调用代码。
您可能会说Catch io.FileNotFoundExeption ex
,然后使用替代文件路径或类似路径,但仍然会引发错误。
也可以使用Throw
代替Throw Ex
来保持整个堆栈的跟踪。 Throw ex从throw语句重新启动堆栈跟踪(我希望这是有道理的)。
#14楼
这不完全等同于完全不处理异常吗?
不完全是,不一样。 它重置异常的堆栈跟踪。 尽管我同意这可能是一个错误,因此是错误代码的示例。
#15楼
实际上,在您发布的代码示例中,捕获异常没有任何意义,因为在捕获上没有执行任何操作,只是重新抛出了异常,实际上,由于丢失了调用栈,这样做的危害大于弊端。
但是,如果发生异常,您将捕获异常以执行某些逻辑(例如,关闭文件锁的sql连接,或仅执行某些日志记录),然后将其扔回到调用代码中进行处理。 在业务层中,这比前端代码更常见,因为您可能希望实现业务层的编码器处理异常。
再次重申,在您发布的示例中,捕获异常没有任何意义。 不要那样做!
#16楼
C#(在C#6之前)不支持VB所支持的CIL“过滤的异常”,因此在C#1-5中,重新引发异常的原因之一是在catch()时您没有足够的信息确定您是否真的想捕获异常。
例如,在VB中,您可以
Try
..
Catch Ex As MyException When Ex.ErrorCode = 123
..
End Try
...将无法使用不同的ErrorCode值处理MyExceptions。 在v6之前的C#中,如果ErrorCode不是123,则必须捕获并重新抛出MyException:
try
{
...
}
catch(MyException ex)
{
if (ex.ErrorCode != 123) throw;
...
}
从C#6.0开始,您可以像使用VB一样进行过滤 :
try
{
// Do stuff
}
catch (Exception e) when (e.ErrorCode == 123456) // filter
{
// Handle, other exceptions will be left alone and bubble up
}