BaseStreamを閉じずにStreamWriterを閉じる方法はありますか?


117

私の根本的な問題は、ときということですusing呼び出すDisposeにはStreamWriter、それはまた、配置BaseStream(同じ問題を持つがClose)。

これには回避策がありますが、ご覧のとおり、ストリームのコピーが必要です。ストリームをコピーせずにこれを行う方法はありますか?

これの目的は、文字列(元はデータベースから読み取られた)の内容をストリームに取得して、サードパーティのコンポーネントがストリームを読み取れるようにすることです。
注意:サードパーティのコンポーネントは変更できません。

public System.IO.Stream CreateStream(string value)
{
    var baseStream = new System.IO.MemoryStream();
    var baseCopy = new System.IO.MemoryStream();
    using (var writer = new System.IO.StreamWriter(baseStream, System.Text.Encoding.UTF8))
    {
        writer.Write(value);
        writer.Flush();
        baseStream.WriteTo(baseCopy); 
    }
    baseCopy.Seek(0, System.IO.SeekOrigin.Begin);
    return baseCopy;
}

使用されます

public void Noddy()
{
    System.IO.Stream myStream = CreateStream("The contents of this string are unimportant");
    My3rdPartyComponent.ReadFromStream(myStream);
}

理想的にはBreakAssociationWithBaseStream、と呼ばれる架空の方法を探しています。

public System.IO.Stream CreateStream_Alternate(string value)
{
    var baseStream = new System.IO.MemoryStream();
    using (var writer = new System.IO.StreamWriter(baseStream, System.Text.Encoding.UTF8))
    {
        writer.Write(value);
        writer.Flush();
        writer.BreakAssociationWithBaseStream();
    }
    return baseStream;
}

これは、同様の質問です:stackoverflow.com/questions/2620851
イェンスGranlund

私はこれをWebRequestからのストリームで実行していましたが、興味深いことに、エンコーディングがASCIIでUTF8ではない場合は閉じることができます。変だ。
tofutim 2013年

tofutim、..私は鉱山がASCIIとしてエンコードされていた、そしてそれはまだ基本的な流れを処分
ジェラルド・オニール

回答:


121

.NET Framework 4.5以降を使用している場合StreamWriterオーバーロードがあり、ライターを閉じたときにベースストリームを開いたままにしておくことができます

4.5より前の.NET Frameworkの以前のバージョンでは、ストリームを所有してStreamWriter いる想定しています。オプション:

  • 廃棄しないでくださいStreamWriter。フラッシュするだけです。
  • Close/ への呼び出しを無視するストリームラッパーを作成しますが、Dispose他のすべてをプロキシします。そこから取得したい場合は、MiscUtilに実装しています。

15
明らかに、4.5のオーバーロードは譲歩とは考えられていませんでした-オーバーロードにはバッファサイズが必要であり、0またはnullにはできません。内部的には128文字が最小サイズであることを知っているので、1に設定します。それ以外の場合、この「機能」は私を幸せにします。
Gerard ONeill、2014

作成leaveOpenStreamWriterにパラメータを設定する方法はありますか?
c00000fd 2018年

@ c00000fd:私が知っていることではありません。
Jon Skeet、2018年

1
@Yepeekai:「ストリームをサブメソッドに渡し、そのサブメソッドがStreamWriterを作成する場合、そのサブメソッドの実行の最後に破棄されます」いいえ、それは事実ではありません。何かが必要な場合にのみ破棄さDisposeれます。メソッド終了はそれを自動的に行いません。ファイナライザがあれば、後でファイナライズされるかもしれませんが、それは同じことではありません。また、どのような危険が予想されているかはまだ明確ではありません。StreamWriterGCによって自動的に破棄される可能性があるため、メソッドからを返すのが安全ではないと考える場合、それは真実ではありません。
Jon Skeet、

1
@Yepeekai:そしてIIRCにStreamWriterはファイナライザがありません-まさにこの理由で、ファイナライザは期待していません。
Jon Skeet、

44

.NET 4.5はそのための新しいメソッドを入手しました!

http://msdn.microsoft.com/EN-US/library/gg712853(v=VS.110,d=hv.2).aspx

public StreamWriter(
    Stream stream,
    Encoding encoding,
    int bufferSize,
    bool leaveOpen
)

どうも!これを知りませんでした。もしそれが.NET 4.5をターゲットにするのに最適な理由になるとしたら!
Vectovox 2014年

22
残念ながら、bufferSizeを設定する必要がないオーバーロードはありません。私はそこのデフォルトに満足しています。私はそれを自分で渡さなければなりません。世界の終わりではありません。
Andy McCluggage 2014

3
デフォルトbufferSize1024です。詳細はこちら
Alex Klaus

35

単純に呼び出すことはありませんDisposeStreamWriter。このクラスが使い捨てである理由は、アンマネージリソースを保持するためではなく、アンマネージリソースを保持できるストリームを破棄できるようにするためです。基になるストリームの寿命が別の場所で処理される場合、ライターを破棄する必要はありません。


2
@Marc、Flushそれがデータをバッファリングする場合、呼び出しは仕事をしませんか?
Darin Dimitrov

3
いいですが、CreateStreamを終了すると、StreamWrtierが収集可能になり、3番目の部分のリーダーがGCと競合するように強制されます。これは、私が残しておきたい状況ではありません。
Binary Worrier 2010

9
@BinaryWorrier:いいえ、競合状態はありません:StreamWriterにはファイナライザがありません(実際にはありません)。
Jon Skeet、2010

10
@Binary Worrier:リソースを直接所有している場合にのみファイナライザが必要です。この場合、StreamWriterは、必要に応じてStream自体がクリーンアップされると想定する必要があります。
Jon Skeet

2
StreamWriterの 'close'メソッドもストリームを閉じて破棄するようです。したがって、ストリームライターをフラッシュする必要がありますが、ストリームライターを閉じたり破棄したりしないでください。ストリームを閉じないため、ストリームを破棄するのと同じことになります。APIからの「ヘルプ」が多すぎる。
Gerard ONeill、2014

5

メモリストリームには、ストリームが閉じている場合でも使用できるToArrayプロパティがあります。To Arrayは、Positionプロパティに関係なく、ストリームの内容をバイト配列に書き込みます。書き込んだストリームに基づいて新しいストリームを作成できます。

public System.IO.Stream CreateStream(string value)
{
    var baseStream = new System.IO.MemoryStream();
    var baseCopy = new System.IO.MemoryStream();
    using (var writer = new System.IO.StreamWriter(baseStream, System.Text.Encoding.UTF8))
    {
        writer.Write(value);
        writer.Flush();
        baseStream.WriteTo(baseCopy); 
    }
    var returnStream = new System.IO.MemoryStream( baseCopy.ToArray());
    return returnStream;
}

返される配列をコンテンツサイズに正しく制限していますか?そのためStream.Positionできない、それが配置されています後に呼び出されます。
Nyerguds

2

StreamWriterの子孫を作成し、そのdisposeメソッドをオーバーライドする必要があります。disposeメソッドに常にfalseを渡すと、ストリームライターが強制的に閉じられなくなります。StreamWriterはcloseメソッドでdisposeを呼び出すだけなので、必要はありません。それをオーバーライドします(もちろん、必要に応じてすべてのコンストラクターを追加できます。私は1つだけ持っています):

public class NoCloseStreamWriter : StreamWriter
{
    public NoCloseStreamWriter(Stream stream, Encoding encoding)
        : base(stream, encoding)
    {
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(false);
    }
}

3
これはあなたが思っていることをしていないと思います。disposingフラグの一部であるパターン。基本クラスのメソッドに常に渡すと、基本的にはファイナライザから呼び出されていることを通知します(明示的に呼び出す場合はそうではありません)。したがって、マネージオブジェクトにはアクセスできません。これが、ベースストリームを破棄しない理由です。ただし、これを達成する方法はハックです。そもそも単純に呼び出さないほうがはるかに簡単です。IDisposablefalseDispose(bool)StreamWriterDispose()Dispose
stakx-2012年

これはまさにシマンテックの話ですが、ストリーミングを完全に最初から書き直すことを除いて、あなたが行うことはすべてハックになるでしょう。確かに、単にbase.Dispose(false)を呼び出すことはまったくできませんが、機能的な違いはなく、私の例の明確さを気に入っています。ただし、これを念頭に置いてください。StreamWriterクラスの将来のバージョンでは、破棄するときにストリームを閉じるだけではないため、dispose(false)を呼び出すと、それも証明されます。しかし、それぞれ自分自身に。
アーロンMurgatroyd

2
別の方法としては、基になるストリームを閉じる代わりにCloseメソッドが何もしない別のストリームを含む独自のストリームラッパーを作成する方法があります。これはハックにはなりませんが、作業量が増えます。
アーロンMurgatroyd 2012

アメージングタイミング:私はちょうど約(おそらくという名前のデコレータクラス、同じことを示唆するたOwnedStream無視していること、Dispose(bool)およびClose)。
stakx-2012年

ええ、上記のコードは迅速でダーティーなメソッドを実行する方法ですが、商用アプリケーションまたは実際に私にとって重要なものを作成している場合は、Streamラッパークラスを使用して正しく実行します。個人的に私は、Microsoftが、ここでミスを犯し、StreamWriterのではなく、基になるストリームをクローズするブール型プロパティを持っているはずだと思いますが、彼らは私が推測するように何ので、私はマイクロソフトではいけない仕事:D
アーロンMurgatroyd
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.