如何正确处置 WebResponse 实例?
通常,人们会编写类似这样的代码来使用 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 示例完全不够。
Normally, one writes code something like this to download some data using a WebRequest.
using(WebResponse resp = request.GetResponse()) // WebRequest request...
using(Stream str = resp.GetResponseStream())
; // do something with the stream str
Now if a WebException is thrown, the WebException has a reference to the WebResponse object, which may or may not have Dispose called (depending on where the exception has happened, or how the response class is implemented) - I don't know.
My question is how one is supposed to deal with this. Is one supposed to be coding very defensively, and dispose of the response in the WebException object (that would be a little weird, as WebException is not IDisposable). Or is one supposed to ignore this, potentially accessing a disposed object or never disposing an IDisposable object?
The example given in the MSDN documentation for WebException.Response is wholly inadequate.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我快速浏览了一下 Reflector,现在可以说:
WebResponse
作为一个抽象类,将其所有关闭/处置行为委托给其派生类。HttpWebResponse
是您几乎肯定在此处使用的派生类,在其 close/dispose 方法中,只关心处理实际的响应流。类状态的其余部分可以留给 GC 来处理。因此,在异常处理方面,您可以安全地执行任何您喜欢的操作,只要:
try
块中的WebResponse
读取响应流时,将其括起来在using
块中。catch
块中的WebException
读取响应流,请将其也包含在using
块中。WebException
本身的处置。I have had a quick peek with Reflector, and can now say:
WebResponse
, being an abstract class, delegates all its closing/disposing behaviour to its derived classes.HttpWebResponse
, being the derived class you are almost certainly using here, in its close/dispose methods, is only concerned with disposing the actual response stream. The rest of the class state can be left to the GC's tender mercies.It follows that it's probably safe to do whatever you like with regard to exception handling, as long as:
WebResponse
in thetry
block, enclose it in ausing
block.WebException
in thecatch
block, enclose it in ausing
block as well.WebException
itself.(几乎)相当于
所以你的对象将始终被处置。
这意味着在您的情况下
ex.Response 将与您的本地 resp 对象是相同的对象,当您到达 catch 处理程序时该对象将被释放。这意味着 DoSomething 正在使用已处置的对象,并且可能会因 ObjectDisposeException 而失败。
is (almost) equivalent to
so your object will always be disposed.
This means that in your case
ex.Response will be the same object as your local resp object, which is disposed when you get to the catch handler. This means that DoSomething is using a disposed object, and will likely fail with an ObjectDisposedException.
HttpWebRequest
在抛出WebException
之前在内部从底层网络流中创建一个内存流,因此不存在与从返回的
。WebResponse
关联的非托管资源>WebException.Response这使得无需对其调用
Dispose()
。事实上,尝试处理WebException.Response
可能会导致头痛和问题,因为您的代码的调用者可能会尝试读取与其关联的属性。不过,最好的做法是处置您拥有的所有
IDisposable
对象。如果您决定这样做,请确保您没有依赖于能够读取WebException.Response
属性和/或其流的代码。最好的方法是处理异常并抛出新类型的异常,这样就不会在可能的情况下将WebException
泄漏给调用者。还可以考虑转向
HttpClient
取代了HttpWebRequest
。免责声明:不暗示任何保证。
HttpWebRequest
internally makes a memory stream off the underlying network stream before throwingWebException
, so there is no unmanaged resource associated with theWebResponse
returned fromWebException.Response
.This makes it unnecessary to call
Dispose()
on it. In fact, trying to disposeWebException.Response
can cause headache and issues because you may have callers of your code that is attempting to read properties associated with it.However it is a good practice that you should dispose any
IDisposable
objects you own. If you decide to do so, make sure you don't have code depending on being able to readWebException.Response
properties and/or its stream. The best way would be you handle the exception and throw new type of exception so that you don't leakWebException
up to the caller when possible.And also consider moving to
HttpClient
which replacesHttpWebRequest
.Disclaimer: No warranty implied.
我非常确定,当您有一个 using 语句时,无论您如何退出 using 块(无论是通过异常、返回还是简单地执行函数),对象都会被释放。
我怀疑如果您让它离开 using 块,您会发现 WebException 内的对象已经被释放。
请记住,处置对象并不一定会阻止以后访问它。稍后尝试调用它的方法可能是不可预测的,导致它自己的异常或非常奇怪的行为(因此我不推荐它)。但即使如此,即使您将其丢弃,对象的很大一部分仍然会被垃圾收集器留下,因此仍然可以访问。处置的目的通常是清理资源句柄(如本例中的活动 TCP 连接),出于性能原因,在垃圾收集器找到它们之前,您实际上无法将其闲置。我提到这一点只是为了澄清它的处置和保存对它的引用的例外并不相互排斥。
I'm pretty sure that when you have a using statement the object is disposed, regardless of how you exit the using block (Be it via exception, return or simply progressing through the function).
I suspect you'll find that the object inside the WebException has already been disposed if you let it leave the using block.
Remember, disposing of an object doesn't necessarily prevent accessing it later. It can be unpredictable to try to call methods on it later, causing exceptions of it's own or very weird behavior (And hence I wouldn't recommend it). But even still a large portion of the object is still left behind for the garbage collector even if you dispose it, and hence is still accessible. The purpose of the dispose is typically to clean up resource handles (Like in this case active TCP connections) that for performance reasons you can't realistically leave laying around until the garbage collector finds them. I only mention this to clarify that it's not mutually exclusive for it to be disposed and the exception to hold a reference to it.
一个非常有趣的问题(尽管值得指出的是,当您退出使用时,WebResponse 对象将被释放)。我的直觉是,只要您不尝试用它做任何“操作”,您是否拥有对此已处置的 WebResponse 对象的引用并不重要。
您可能仍然可以出于日志记录目的访问实例上的某些属性(例如 ResponseUri),而不会出现
ObjectDisposeException
,但异常所持有的总体引用不存在,因此您可以继续使用该实例。我有兴趣看看其他人怎么说。
A very interesting question (although worth pointing out that the WebResponse object will have been disposed as you exit the using). My gut feeling is that it doesn't really matter that you've got a reference to this disposed WebResponse object as long as you don't try to do anything "operational" with it.
You can probably still access certain properties on the instance for logging purposes (such as the ResponseUri) without getting an
ObjectDisposedException
, but overall reference held by the exception is not there so you can continue using the instance.I'd be interested to see what others say.
我在 EF DB 连接中遇到类似的情况。
所以我实际上创建了一个连接列表。
游戏结束时,我循环处理所有这些。
I get similar cases in EF DB connections.
So i actually create a connections list.
At the end of game , i loop dispose all of them.