没有一种简单的方法可以安全地返回 Stream 而不会导致资源泄漏。主要问题是处理 WebResponse:
public Stream Load(string term)
{
var url = CreateSearchUrl(term);
var webRequest = (HttpWebRequest)WebRequest.Create(url);
var webResponse = webRequest.GetResponse(); // whoops this doesn't get disposed!
return new GZipStream(webResponse.GetResponseStream(), CompressionMode.Decompress);
}
关闭 WebResponse 实际上比关闭响应流更重要,因为关闭 WebResponse 会隐式关闭响应流。
我知道让 WebResponse 与 Stream 一起处理的唯一方法是在 GZipStream 周围实现一个装饰器,该装饰器在处理 WebResponse(以及 GZipStream)时处理它。虽然这样可行,但代码量很大:
class WebResponseDisposingStream : Stream
{
private readonly WebResponse response;
private readonly Stream stream;
public WebResponseDisposingStream(WebResponse response, Stream stream)
{
if (response == null)
throw new ArgumentNullException("response");
if (stream == null)
throw new ArgumentNullException("stream");
this.response = response;
this.stream = stream;
}
public override void Close()
{
this.response.Close();
this.stream.Close();
}
// override all the methods on stream and delegate the call to this.stream
public override void Flush() { this.stream.Flush(); } // example delegation for Flush()
// ... on and on for all the other members of Stream
}
也许更好的方法是继续传递样式,其中使用 Stream 的代码作为委托传入:
public void Load(string term, Action<Stream> action)
{
var url = CreateSearchUrl(term);
var webRequest = (HttpWebRequest)WebRequest.Create(url);
using (var webResponse = webRequest.GetResponse())
using (var responseStream = webResponse.GetResponseStream())
using (var gzipStream = new GZipStream(responseStream, CompressionMode.Decompress))
{
action(gzipStream);
}
}
现在调用者只需传入应该对 Stream 执行的操作。下面将长度打印到控制台:
Load("test", stream => Console.WriteLine("Length=={0}", stream.Length));
最后一点:如果您不知道,HTTP 内置了对压缩的支持。有关详细信息,请参阅Wikipedia。 HttpWebRequest 通过AutomaticDecompression 属性内置了对HTTP 压缩的支持。使用 HTTP 压缩基本上可以使压缩对您的代码透明,并且还可以更好地与 HTTP 工具(浏览器、提琴手等)配合使用。