抛出与捕获的类型相同的C#异常?

时间:2010-10-05 12:58:17

标签: c# exception-handling

为什么(如果有的话)这是一个坏主意?

class Program
{
  static void Main(string[] args)
  {
     try
     {
        throw new NotImplementedException("Oh dear");
     }
     catch (Exception ex)
     {
        throw NewException("Whoops", ex);
     }
  }

  // This function is the salient bit here
  public static Exception NewException(String message, Exception innerException)
  {
     return Activator.CreateInstance(innerException.GetType(), message, innerException) as Exception;
  }
}

这里重要的一点是该函数创建了与“innerException”相同类型的异常。

我在想......“哦......发生了一个异常。我实际上无法在这里处理它,但我可以添加一些额外的信息,并重新抛出。也许是另一个处理程序,更高的通话连锁店可以处理它。“

一个恰当的例子可能是某种SQL错误。我可能无法在调用时处理异常,但可能希望添加一些额外的“上下文”信息,例如“我正在调用它,并传递它”。

似乎将调用链作为最初引发的类型的异常传递回来可能是有用的,而不是“Exception”或“ApplicationException”。我知道我可以创建自己的自定义异常类,但是当你已经有一个很好的特定异常时它似乎没有增加任何东西。

当然,我可能错了。这可能是一件非常有用的事情......但是一个小小的声音并不是这样。

-----编辑-----

为了辩论,请考虑以下两个函数的效果(使用上面的代码):

这......经常出现:

  static int SalesTotal(int customerNumber)
  {
     try
     {
        throw new DivideByZeroException(); // something you didn't expect
     }
     catch (Exception ex)
     {
        throw new ApplicationException("Unable to calculate sales for customer " + customerNumber, ex);
     }
  }

与此相比......

  static int SalesTotal(int customerNumber)
  {
     try
     {
        throw new DivideByZeroException(); // something you didn't expect
     }
     catch (Exception ex)
     {
        throw NewException("Unable to calculate sales for customer " + customerNumber, ex);
     }
  }

7 个答案:

答案 0 :(得分:12)

所有异常都有一个Data属性,您可以添加其他数据。无需创建新的异常。只需捕获现有异常并添加您的信息,然后重新抛出异常即可。

这样你就可以吃蛋糕了。 :)

http://msdn.microsoft.com/en-us/library/system.exception_members(v=VS.90).aspx

答案 1 :(得分:4)

对于像这样的通用方法,创建新的异常类型不是一个好的选择,因为现有代码将无法对特定错误做出反应。 (但API边界处的翻译例外很有用)。

创建相同类型的新异常似乎很危险。您使用的CreateInstance重载是否会将innerException中的所有字段复制到新的外部异常中?如果堆栈上方的异常处理程序依赖于解析Message属性,该怎么办?如果异常构造函数有副作用怎么办?

恕我直言,你真正在做的就是记录,你可能会更好地实际记录和重新投掷。

答案 2 :(得分:3)

我认为你应该创建自己的自定义异常类型。你要添加什么样的信息?例外的捕手如何知道它有额外的信息?如果您使用自定义类型,他们将有额外的属性/方法来查看或调用。

答案 3 :(得分:1)

根本不要抓住它,只是让它泡起来 - 没有必要重新发明轮子。

您的方法不仅对其他人来说不直观,而且您还将从第一个异常中丢失原始堆栈跟踪。

答案 4 :(得分:0)

在某些情况下可能有意义。如果你认为MyClass.MyFunction是程序集的公共“外部”,那么它内部发生的是调用代码的黑盒子。因此,就调用代码所发生的异常而言,可能更合理的是,最合理的异常类型可能与捕获的类型相同。

但我会谨慎。在这种情况发生的大多数情况下,要么你应该能够捕捉到异常将要发生,并且先发制人(如果你要扔掉,越快越好)或者你抛出的异常就是'确实是正确的类型(如果您的代码找不到它需要的文件,那就是FileNotFoundException,但如果文件是实现细节,那么它不是调用代码的FileNotFoundException),否则原始异常会使完美的感觉,它应该被允许通过throw;打电话或重新播放。

所以,并不总是一个坏主意,但通常很糟糕,以至于看到它总是一件可疑的事情。

答案 5 :(得分:0)

恕我直言,拥有一些自定义异常类型可能会很好,其中一个异常层次结构表示“此操作失败,但系统状态就好像从未尝试过”,另一个异常说“操作失败,系统状态受到干扰,但它可能被解开“,另一个用于”操作失败,并且展开失败;系统状态不可信任。“在某些情况下,可以捕获一个异常类并将其作为另一个异常重新抛出(例如,如果应该完成恢复系统状态的操作失败,即使从该操作的角度来看没有任何干扰,也无法恢复状态可能会让它受到干扰;相反,如果一个操作使系统状态受到干扰但是一个捕获处理程序恢复了状态,它可能会重新抛出为“操作失败但系统状态不受干扰”。

我非常不喜欢尝试创建盲目匹配抛出的异常类型的异常实例,因为抛出的异常可能包含处理程序预期填充的字段。我不确定为什么微软会让例外“几乎”不可改变。如果要求异常是不可变的并支持使用语义提供的不可变性支持克隆,那将会更好。

答案 6 :(得分:0)

只捕获您处理的异常,例如,您不希望从数据层向业务层抛出SqlException(捕获它,尝试处理它并抛出DataLayerException或类似的东西)。