当前位置:  开发笔记 > 编程语言 > 正文

在C#中重新抛出异常的正确方法是什么?

如何解决《在C#中重新抛出异常的正确方法是什么?》经验,为你挑选了7个好方法。

我有一个问题,源于我的伙伴以不同于我的方式做事.

这样做更好:

try
{
    ...
}
catch (Exception ex)
{
    ...
    throw;
}

或这个:

try
{
    ...
}
catch (Exception ex)
{
    ...
    throw ex;
}

他们做同样的事情吗?这个比那个好吗?



1> Torbjörn Gyl..:

您应该始终使用以下语法来重新抛出异常,否则您将踩踏堆栈跟踪:

throw;

如果您打印"throw ex"产生的跟踪,您将看到它在该语句上结束,而不是在异常的真实来源.

基本上,使用"throw ex"应被视为刑事犯罪.


有一个fxcop规则:RethrowToPreserveStackDetails http://msdn.microsoft.com/en-us/library/ms182363(VS.80).aspx
这是真的吗?我认为信息仍然存在于innerException中.哦,我明白了.你确实丢失了抛出ex的信息,但是不会抛出新的异常("foo",ex).
从.Net 4.5开始,必须使用的是:ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); /sf/ask/17360801/
不能同意更多.5年以上经验丰富的开发人员并不真正了解异常处理最佳实践,这真是令人惊讶.我无法解释当我看到try/catches要么吞下异常而没有记录或重新抛出异常而没有在catch中做任何有价值的事情时,我想解释我的眼睛.
有趣,对我不起作用.堆栈跟踪丢失.throw ex或throw之间没有区别,结果相同.
我用一个简单的除以零测试所有,我发现这不是最好的.您丢失了例外的行号.您将只获得throw语句的行.应该使用throw new Exception("有用消息",ex); 这会创建一个内部异常,保留原始堆栈和行号.

2> RB...:

我的偏好是使用

try 
{
}
catch (Exception ex)
{
     ...
     throw new Exception ("Put more context here", ex)
}

这样可以保留原始错误,但允许您放置更多上下文,例如对象ID,连接字符串等.我的异常报告工具通常会有5个链式异常报告,每个报告更多细节.


通常我会抛出特定的BusinesssLayer异常(例如BusinessObjectInstantionFailed类似的东西).我更喜欢链式异常,因为我希望大多数其他开发人员假设异常可能被链接,但他们可能不会询问其他属性.
同意 - 但是为什么你甚至会首先捕获异常,除非你可以执行一个有用的操作,比如添加上下文?问题预先假定此时无法处理异常.
我发现这种技术对于在一些递归XML反序列化代码中捕获和重新抛出异常特别有用.如果我捕获然后执行`抛出新异常(string.Format("反序列化元素{0}","element.Name",ex);`那么如果我在XML解析中遇到崩溃,那么顶级异常handler打印所有的InnerExceptions,我得到了它当前所有嵌套XML节点名称的完整描述,仅仅通过查看对递归函数进行大量调用的callstack就不明显了. .
@kami原始的堆栈跟踪在内部异常中仍然可用.是否包装异常或只是`throw;`取决于具体情况.

3> 小智..:

如果抛出异常,并一个变量(第二个例子)的堆栈跟踪将包括抛出异常的原始方法.

在第一个示例中,将更改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
    }


代码是正确的,但描述它的文本是向后的.

4> Quibblesome..:

第一个保留异常的原始堆栈跟踪,第二个跟踪当前位置.

因此第一个是BY FAR越好.



5> Jon Hanna..:

我知道这是一个老问题,但我会回答它,因为我不得不在这里不同意所有的答案.

现在,我同意大多数时候你要么做一个简单的事情throw,尽可能多地保存关于出错的信息,或者你想抛出一个新的异常,可能包含那个作为内部异常,或者不是,取决于你想知道导致它的内部事件的可能性.

但有一个例外.在某些情况下,方法将调用另一个方法,并且在内部调用中导致异常的条件应被视为外部调用上的相同异常.

一个示例是使用另一个集合实现的专用集合.让我们说这是一个DistinctList包装List但拒绝重复的项目.

如果某人调用ICollection.CopyTo了您的集合类,那么它可能只是CopyTo对内部集合的直接调用(例如,所有自定义逻辑仅适用于添加到集合或设置它).现在,该调用将抛出的条件与您的集合应该抛出的条件完全相同,以匹配文档ICollection.CopyTo.

现在,你可能根本就没有抓住这个执行,让它通过.虽然用户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;
  }
}


不就是不.抛出异常时不要隐藏"实现细节".当他们在你的源代码中查找异常的解释并发现它本身不会产生异常时,这只会让人们感到困惑.使用原始堆栈跟踪,您至少可以找到生成异常的位置,以确切了解它被抛出的原因.

6> Vinod T. Pat..:

你应该总是使用"扔"; 重新抛出.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"覆盖堆栈跟踪的原因.



7> Mendelt..:

首先是更好。如果尝试调试第二个并查看调用堆栈,您将看不到原始异常的来源。如果确实需要重新抛出,有一些技巧可以使调用栈保持完整(尝试​​搜索,之前已经有人回答了)。

推荐阅读
刘美娥94662
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有