我经常遇到需要捕获实现接口时抛出的异常情况。当不同的实现处理完全不同类型的设备等时,这通常会变得棘手:根据具体实现,抛出的异常种类和数量都大相径庭。以下是一个典型的例子:
interface DataStream
{
Data ReadNext();
}
class DeserializingFileDataStream : DataStream
{
//this one does file operations + serialization,
//so can throw eg IOException, SerializationException, InvalidOperationException, ...
}
class NetworkDataStream : DataStream
{
//get data over tcp
//so throws IOException, SocketException
}
class HardwareDeviceDataStream : DataStream
{
//read from a custom hardware device implemented in unmanaged code
//so throws mainly custom exceptions
}
很可能所有这些也会抛出ArgumentException等异常,但我不感兴趣捕获这些异常:在这种情况下,它们会表示编程错误。然而,当文件损坏、网络电缆被拔掉或自定义设备失控时,我不希望程序崩溃,因此其他异常应该被处理。
我已经尝试了一些解决方案,但都没有特别满意,所以问题是:有没有一些常见的做法/模式来处理这种情况?请具体说明,不要告诉我去“看看ELMAH”。以下是我过去使用过的一些方法:
- `catch( Exception )` 很明显知道问题在哪里。 - `TryAndCatchDataStream( Action what, Action<Exception> errHandler )` 方法由一个 try 后跟任何实现的任何感兴趣的异常的 catch 组成。这意味着当实现更改或添加/删除时必须更新。 - 在接口上放置一个注释,说“只能抛出 DataStreamExceptions”,并在所有实现中确保遵循此规则,通过捕获任何感兴趣的异常并将其包装在 DataStreamException 中,然后抛出。不太糟糕,但会给每个实现增加噪音。
然后我有一个关于异常处理的第二个更一般的问题(不认为有必要为此发布一个单独的帖子):我有几种情况是方法 A 调用 B,B 调用 C,C 调用 D,而 D 抛出 SomeException 但是 A 的调用者捕获了异常。这不是代码有问题的信号吗?因为为了能够这样做,A 的调用者需要知道 A 最终会调用 D。除非 A 记录它可以抛出 SomeException;但在两种情况下,这都意味着当更新 A 的仅仅是实现细节(即让它调用与 D 不同的东西)时,这个变化对 A 的用户可见。