如何正确处理WebResponse实例?

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

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

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

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

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

  • WebResponse是一个抽象类,它将所有关闭/处置行为委托给它的派生类。
  • HttpWebResponse ,作为你几乎肯定在这里使用的派生类,在它的close / dispose方法中,只关心处理实际的响应流。 其余的阶级国家可以留给GC的招标怜悯。

因此,只要符合以下条件,就exception处理做任何你喜欢的事情都可能是安全的:

  • 当您从try块中的WebResponse读取响应流时,将其包含在using块中。
  • 如果catch块中的WebException读取响应流,也将它包含在using块中。
  • 无需担心处理WebException本身。
 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而失败。

我很确定当你有一个using语句时,无论你如何退出using块(无论是通过exception,返回还是只是通过函数进行),都会释放对象。

我怀疑如果让它离开使用块,你会发现WebException中的对象已被处理掉了。

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

在抛出WebException之前, HttpWebRequest内部使内存流脱离底层网络流,因此没有与WebException.Response返回的WebResponse关联的非托管资源。

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

但是,您应该处置您拥有的任何IDisposable对象是一个好习惯。 如果您决定这样做,请确保您没有代码,具体取决于能否读取WebException.Response属性和/或其流。 最好的方法是处理exception并抛出新类型的exception,以便在可能的情况下不会将WebException泄漏给调用者。

并且还考虑转移到替换HttpWebRequest HttpClient

免责声明:不提供任何保证。

一个非常有趣的问题(虽然值得指出,当您退出使用时,WebResponse对象被处理掉)。 我的直觉是,只要您不尝试对其进行任何“操作”操作,您就可以引用此处理的WebResponse对象并不重要。

可能仍然可以访问实例上的某些属性以进行日志记录(例如ResponseUri)而不会获得ObjectDisposedException ,但exception所持有的整体引用不存在,因此您可以继续使用该实例。

我有兴趣看看别人怎么说。

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

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

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