リソース リークを起こさずに 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 で破棄する唯一の方法は、破棄時に WebResponse (および GZipStream) を破棄する 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));
最後に 1 つ: ご存じないかもしれませんが、HTTP には圧縮のサポートが組み込まれています。詳細については、ウィキペディアを参照してください。HttpWebRequest には、AutomaticDecompressionプロパティによる HTTP 圧縮のサポートが組み込まれています。HTTP 圧縮を使用すると、基本的に圧縮がコードに対して透過的になり、HTTP ツール (ブラウザー、フィドラーなど) との連携も向上します。