通常,人们会使用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对象? WebException.Response的MSDN文档中给出的示例完全不合适。
答案 0 :(得分:15)
我快速浏览了Reflector,现在可以说:
WebResponse
,作为一个抽象类,将其所有关闭/处置行为委托给其派生类。HttpWebResponse
,作为派生类,你几乎肯定在这里使用它的close / dispose方法,只关心处理实际的响应流。其余的阶级状态可以留给GC的怜悯。因此,只要符合以下条件,就可以安全地做任何你喜欢的异常处理:
WebResponse
块中的try
读取响应流时,请将其封装在using
块中。WebException
块中的catch
读取了响应流,请将其也包含在using
块中。WebException
。答案 1 :(得分: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而失败。
答案 2 :(得分:2)
我很确定当你有一个using语句时,无论你如何退出using块(无论是通过异常,返回还是只是通过函数进行),都会处理该对象。
我怀疑如果让它离开使用块,你会发现WebException中的对象已被处理掉。
请记住,处置对象并不一定会阻止以后访问它。尝试稍后调用方法可能是不可预测的,导致它自己或非常奇怪的行为的异常(因此我不推荐它)。但即使你丢弃它,即使仍然有很大一部分对象仍留在垃圾收集器中,因此仍可访问。 dispose的目的通常是清理资源句柄(就像在这种情况下是活动的TCP连接),出于性能原因,在垃圾收集器找到它们之前,您无法实际放置。我只是提到这一点,以澄清它的处理并不是相互排斥的,而是提及它的例外。
答案 3 :(得分:2)
HttpWebRequest
在抛出WebException
之前在内部从底层网络流中创建内存流,因此没有与WebResponse
返回的WebException.Response
关联的非托管资源。
这使得无需在其上调用Dispose()
。实际上,尝试处置WebException.Response
可能会导致头痛和问题,因为您的代码调用者可能会尝试读取与之关联的属性。
但是,您应该处置您拥有的任何IDisposable
个对象,这是一个好习惯。如果您决定这样做,请确保您没有代码,具体取决于能够阅读WebException.Response
属性和/或其流。最好的方法是处理异常并抛出新类型的异常,以便在可能的情况下不会将WebException
泄漏给调用者。
并考虑转移到HttpClient
,取代HttpWebRequest
。
免责声明:不提供任何保证。
答案 4 :(得分:0)
一个非常有趣的问题(虽然值得指出,当您退出使用时,WebResponse对象将处理掉)。我的直觉是,只要您不尝试对其进行任何“操作”操作,您就可以引用此处理的WebResponse对象并不重要。
您可能仍然可以访问实例上的某些属性以进行日志记录(例如ResponseUri)而不会获得ObjectDisposedException
,但异常所持有的整体引用不存在,所以您可以继续使用该实例。
我有兴趣看看别人怎么说。
答案 5 :(得分:0)
我在EF DB连接中遇到类似的情况。
所以我实际创建了一个连接列表。
在游戏结束时,我循环处理所有这些。