C# 如何正确处置WebResponse实例?

C# 如何正确处置WebResponse实例?,c#,.net,idisposable,webresponse,C#,.net,Idisposable,Webresponse,通常,编写类似这样的代码来使用WebRequest下载一些数据 using(WebResponse resp = request.GetResponse()) // WebRequest request... using(Stream str = resp.GetResponseStream()) ; // do something with the stream str 现在,如果抛出WebException,WebException将引用WebResponse对象,

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

using(WebResponse resp = request.GetResponse())  // WebRequest request...
   using(Stream str = resp.GetResponseStream())  
      ; // do something with the stream str
现在,如果抛出WebException,WebException将引用WebResponse对象,该对象可能调用了Dispose,也可能没有调用Dispose(取决于异常发生的位置,或者响应类的实现方式)——我不知道

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

我非常确定,当您有一个using语句时,不管您如何退出using块(无论是通过exception、return还是简单地通过函数前进),对象都会被释放

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


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

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

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

我很想看看别人怎么说

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);
}

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

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

  • WebResponse
    ,作为一个抽象类,将其所有关闭/处理行为委托给其派生类
  • HttpWebResponse
    ,作为您在这里几乎可以肯定使用的派生类,在其close/dispose方法中,只涉及处理实际的响应流。其余的阶级状态可以留给总司令来决定
因此,在异常处理方面,您可以随心所欲,只要:

  • try
    块中的
    WebResponse
    读取响应流时,请使用
    块将其括在
  • 如果您从
    catch
    块中的
    WebException
    读取响应流,请使用
    块将其括在
  • 无需担心如何处理
    WebException
    本身

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

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


在游戏结束时,我循环处理它们。

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

这使得无需对其调用
Dispose()
。事实上,试图处理
WebException.Response
可能会引起头痛和问题,因为您的代码调用方可能正试图读取与之关联的属性

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

也考虑移动到替换<代码> HttpWebRequest < /C> > < /P>


免责声明:不作任何保证。

确实如此<代码>使用
是try{}finally{}的语法糖,对象将始终在finally块中处理。GetResponse中发生异常如何?结果将不会分配给resp变量,因此从表达式返回,并由using语句处理。我知道一个人可以访问一个已处理的对象,但是,一个人实际上不应该访问。如果这就是这个api的意图,那么这个api不是设计得“糟糕”吗?不,如果GetResponse抛出任何对象,那么将不会处理任何对象。但您无法知道是否已创建了对象,因为只有GetResponse的内部成员才能知道,因此GetResponse有责任确保在这种情况下没有泄漏。作为一个示例,仅说明我的问题WebRequest test=WebRequest.Create(“);尝试使用{(WebResponse resp=test.GetResponse());}