重新抛出异常时保留其类型

4
我正在编写一个可以对多个流进行操作的类。以下是我目前正在做的示例。
Dictionary<int, int> dict = new Dictionary<int, int>(_Streams.Count);
for (int i = 0; i < _Streams.Count; i++)
{
    try
    {
        dict.Add(i, _Streams[i].Read(buffer, offset, count));
    }
    catch (System.IO.IOException e)
    {
        throw new System.IO.IOException(String.Format("I/O exception occurred in stream {0}", i), e);
    }
    catch (System.NotSupportedException e)
    {
        throw new System.NotSupportedException(String.Format("The reading of the stream {0} is not supported", i), e);
    }
    catch (System.ObjectDisposedException e)
    {
        throw new System.ObjectDisposedException(String.Format("Stream {0} is Disposed", i), e);
    }
}
int? last = null;
foreach (var i in dict)
{
    if (last == null)
        last = i.Value;
    if (last != i.Value)
        throw new ReadStreamsDiffrentExecption(dict);
    last = i.Value;
}
return (int)last;

我希望将我的代码简化为

Dictionary<int, int> dict = new Dictionary<int, int>(_Streams.Count);
for (int i = 0; i < _Streams.Count; i++)
{
    try
    {
        dict.Add(i, _Streams[i].Read(buffer, offset, count));
    }
    catch (Exception e)
    {
        throw new Exception(String.Format("Exception occurred in stream {0}", i), e);
    }
}
int? last = null;
foreach (var i in dict)
{
    if (last == null)
        last = i.Value;
    if (last != i.Value)
        throw new ReadStreamsDiffrentExecption(dict);
    last = i.Value;
}
return (int)last;

然而,如果有人试图捕获特定的异常,我的包装器将隐藏Read抛出的异常。我该如何保留异常类型、添加额外信息,但不需要为try块中的每种可能情况编写处理程序。


你的第一个版本很完美,如果消费者想要使用 InnerException 做些事情,他们可以检查一下,而你已经提供了他们所需要的一切。 - Nick Craver
5个回答

3
我建议根本不要捕获这些异常...
你添加的信息(大部分)可以从堆栈转储中获取。
您可以使用catch-and-wrap将其转换为特定于库的异常:
 catch (Exception e)
 {
    throw new ReadStreamsErrorExecption(
      String.Format("Exception occurred in stream {0}", i), e);
 }

我尝试避免依赖堆栈转储的原因是为了下一个版本,我计划对读写操作进行线程化处理。 - Scott Chamberlain
除非您提出的代码丢弃了原始的堆栈跟踪,否则不然。 - JeffH
@JeffH,我提出了两种方法,第一种是:不要动它。第二种方法会让你有两个堆栈跟踪需要搞明白。 - H H

1
一个鲜为人知的.NET技巧是,你可以在不包装异常的情况下添加信息。每个异常都有一个名为.Data的字典,你可以将其填充到其他信息中,例如:
try
{
   ...
}
catch (FileNotFoundException ex)
{
   ex.Data.Add("filename", filename);
   throw;
}

现在,在您的顶级异常处理代码中,您可以将异常及其关联的字典转储到日志文件或异常数据库中,从而获得比以前更多的信息。

在ASP.NET应用程序中,您可能希望在让应用程序错误处理程序接管之前,将URL、用户名、引荐人、cookie的内容等添加到.Data字典中。


1

我认为你在处理异常的方式上有些问题。

  1. 你不应该抛出基本的 Exception 类,而是应该抛出更具体的异常,以便他们可以处理它。
  2. id 值是否真的对诊断功能有价值?

我建议你重新审视你的代码,并看看是否真的需要包装异常。


1

我认为第一个版本更易读,更能表达我的意思。这就是异常处理应该写的方式。


1
通常我从Eric Lipperts的博客中学到的规则是,只有在你要对异常进行处理时才应该捕获它。
在这里,你只是用一个新的消息重新抛出了异常。除非你要尝试从错误中恢复,否则就让客户端自己处理异常。如果要尝试从错误中恢复,则添加一个
throw;

如果你需要将异常向上冒泡,因为你无法处理它。

Ian,有时候“更改”异常是很有成效的,这就是为什么Exception类有InnerException属性的原因。这样做并不是真正的“处理”它,而且throw;也不适用。 - H H

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接