22

通常,编写类似这样的代码来使用 WebRequest 下载一些数据。

using(WebResponse resp = request.GetResponse())  // WebRequest request...
   using(Stream str = resp.GetResponseStream())  
      ; // do something with the stream str

现在,如果抛出 WebException,则 WebException 具有对 WebResponse 对象的引用,该对象可能调用也可能不调用 Dispose(取决于异常发生的位置,或响应类的实现方式)——我不知道。

我的问题是应该如何处理这个问题。是否应该进行非常防御性的编码,并在 WebException 对象中处理响应(这会有点奇怪,因为 WebException 不是 IDisposable)。还是应该忽略这一点,可能访问已处置的对象或从不处置 IDisposable 对象?MSDN 文档中针对 WebException.Response 给出的示例是完全不够的。

4

6 回答 6

16

我快速浏览了 Reflector,现在可以说:

  • WebResponse,作为一个抽象类,将其所有关闭/处置行为委托给其派生类。
  • HttpWebResponse,作为您几乎可以肯定在这里使用的派生类,在它的 close/dispose 方法中,只关心处理实际的响应流。其余的类状态可以留给 GC 的温柔仁慈。

因此,在异常处理方面做任何你喜欢的事情可能是安全的,只要:

  • WebResponse当您从块中读取响应流时try,请将其包含在using块中。
  • 如果WebException您从块中读取响应流catch,请将其也包含在using块中。
  • 无需担心WebException自行处置。
于 2009-12-11T11:20:46.107 回答
3
using (var x = GetObject()) {
     statements;
}

(几乎)等于

var x = GetObject();
try {
    statements;
}
finally {
     ((IDisposable)x).Dispose();
}

因此您的对象将始终被处置。

这意味着在您的情况下

try {
    using (WebResponse resp = request.GetResponse()) {
        something;
    }
}
catch (WebException ex) {
    DoSomething(ex.Response);
}

ex.Response 将是与本地 resp 对象相同的对象,该对象在您到达 catch 处理程序时被释放。这意味着 DoSomething 正在使用已处置的对象,并且可能会因 ObjectDisposedException 而失败。

于 2009-12-11T11:09:22.793 回答
3

HttpWebRequest在抛出之前在内部从底层网络流中生成一个内存流WebException,因此没有与WebResponse返回的 from关联的非托管资源WebException.Response

这使得没有必要调用Dispose()它。事实上,尝试处理WebException.Response可能会导致头痛和问题,因为您的代码的调用者可能会尝试读取与其关联的属性。

然而,最好的做法是处置IDisposable您拥有的任何对象。如果您决定这样做,请确保您没有代码,这取决于能够读取WebException.Response属性和/或其流。最好的方法是你处理异常并抛出新类型的异常,这样你就不会WebException在可能的情况下泄漏给调用者。

并且还考虑移动到HttpClient哪个替换HttpWebRequest

免责声明:不暗示任何保证。

于 2017-03-31T02:11:34.923 回答
2

我很确定当你有一个 using 语句时,无论你如何退出 using 块(无论是通过异常,返回还是简单地通过函数),对象都会被释放。

我怀疑如果你让它离开 using 块,你会发现 WebException 内的对象已经被释放。

请记住,处理对象并不一定会阻止以后访问它。稍后尝试对其调用方法可能是不可预测的,从而导致它自己的异常或非常奇怪的行为(因此我不推荐它)。但是,即使您处置了对象,仍有很大一部分对象仍留给垃圾收集器,因此仍然可以访问。dispose 的目的通常是清理资源句柄(如本例中的活动 TCP 连接),出于性能原因,在垃圾收集器找到它们之前,您实际上无法将其放置在周围。我提到这一点只是为了澄清它的处置和持有对它的引用的例外情况并不相互排斥。

于 2009-12-11T11:02:59.560 回答
0

一个非常有趣的问题(尽管值得指出的是 WebResponse 对象在您退出使用时被释放)。我的直觉是,只要您不尝试对它执行任何“操作”操作,您获得对已处理的 WebResponse 对象的引用并不重要。

可能仍然可以访问实例上的某些属性以用于记录目的(例如 ResponseUri)而无需获取ObjectDisposedException,但异常所持有的整体引用不存在,因此您可以继续使用该实例。

我很想看看别人怎么说。

于 2009-12-11T11:05:47.123 回答
0

我在 EF DB 连接中遇到了类似的情况。

所以我实际上创建了一个连接列表。

在游戏结束时,我循环处理所有这些。

于 2015-03-05T00:41:07.877 回答