有什么方法可以关闭StreamWriter而不关闭其BaseStream吗?


117

我的根本问题是,在using调用DisposeStreamWriter,它还会处理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;
}


我正在使用来自WebRequest的流进行此操作,有趣的是,如果编码是ASCII而不是UTF8,则可以将其关闭。奇怪的。
tofutim

tofutim,我有我的编码为ASCII,它仍然部署底层流的..
杰拉德·奥尼尔

Answers:


121

如果使用的是.NET Framework 4.5或更高版本,则存在StreamWriter重载,使用该重载可以要求在关闭writer时要求基本流保持打开状态

在4.5之前的.NET Framework的早期版本中,StreamWriter 假定它拥有流。选项:

  • 不要处理StreamWriter; 冲洗一下即可。
  • 创建一个流包装器,该包装器将忽略对Close/的调用,Dispose但将代理所有其他内容。如果您想从那里获取它,我可以在MiscUtil中实现。

15
显然,4.5重载不是一个可以考虑的让步-重载需要缓冲区大小,该大小不能为0或为null。在内部,我知道最小字符数为128个,因此我将其设置为1。否则,此“功能”使我感到高兴。
Gerard ONeill 2014年

创建leaveOpen后是否可以设置该参数StreamWriter
c00000fd

@ c00000fd:我不知道。
乔恩·斯基特

1
@Yepeekai:“如果我将流传递给子方法,并且该子方法创建StreamWriter,它将在该子方法执行结束时处理”。仅当有需要时才将其丢弃Dispose。方法结尾不会自动执行此操作。它可完成以后,如果它有一个终结,但是这不是同样的事情-它仍然不清楚你在期待什么危险。如果您认为StreamWriter从方法中返回a是不安全的,因为它可能会被GC自动处置,那是不对的。
乔恩·斯基特

1
@Yepeekai:而且IIRC StreamWriter没有终结器-正是出于这个原因,我不希望这样做。
乔恩·斯基特

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的重载。我对那里的默认设置感到满意。我必须自己通过。不是世界末日。
2014年

3
默认bufferSize值为1024详细信息在这里
亚历克斯·克劳斯

35

根本就没有叫DisposeStreamWriter。此类可丢弃的原因不是因为它持有非托管资源,而是允许处置本身可能持有非托管资源的流。如果基础流的生命在其他地方处理,则无需处置编写器。


2
@Marc,Flush万一它缓冲数据就不会调用该作业吗?
Darin Dimitrov

3
很好,但是一旦退出CreateStream,就可以收集StreamWrtier,从而迫使第三方阅读器与GC竞争,这不是我想留下的情况。还是我错过了某些东西?
Binary Worrier,2010年

9
@BinaryWorrier:不,没有竞争条件:StreamWriter没有终结器(实际上应该没有终结器)。
乔恩·斯基特

10
@Binary Worrier:仅当您直接拥有资源时,才应该有终结器。在这种情况下,StreamWriter应该假定Stream会在需要时自行清理。
乔恩·斯基特

2
看来StreamWriter的“关闭”方法也可以关闭并处理流。因此,必须冲洗但不关闭或处置流写入器,这样它才不会关闭流,这相当于处置流。这里的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方法,方法是始终将false始终传递给dispose参数,这将迫使流编写器不关闭,StreamWriter只是在close方法中调用dispose,因此无需覆盖它(当然,如果需要,您可以添加所有构造函数,我只有一个):

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

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

3
我相信这并没有按照您的想法做。该disposing标志为的一部分IDisposable模式。始终传递falseDispose(bool)基类的方法基本上会发出信号,StreamWriter表明已从终结器中调用该方法(当您Dispose()显式调用时并非如此),因此不应访问任何托管对象。就是为什么它不会处理基本流的原因。但是,实现此目标的方法是破解。根本不打来电话会容易得多Dispose
stakx-不再贡献2012年

那就是Symantec的意思,除了完全从头重写流媒体之外,您要做的所有事情都会成为黑客。当然,您完全可以根本不调用base.Dispose(false),但是在功能上没有任何区别,我喜欢我的示例的清晰度。但是,请记住这一点,StreamWriter类的未来版本可能不仅仅在处理时关闭流,还可以做更多的事情,因此将来调用dispose(false)也可以证明这一点。但每一个他自己。
亚伦·穆加特罗伊德

2
另一种方法是创建自己的流包装器,其中包含另一个流,其中Close方法只是不执行任何操作而不是关闭基础流,这不是hack,而是更多工作。
亚伦·穆加特罗伊德

惊人的时机:我正要提出相同的建议(装饰器类,可能名为OwnedStream,它忽略Dispose(bool)Close)。
stakx-

是的,上面的代码是我为快速而肮脏的方法而执行的操作,但是,如果我要进行商业应用程序或实际对我而言很重要的事情,则可以使用Stream包装器类正确地进行操作。我个人认为微软在这里犯了一个错误,streamwriter应该具有一个布尔属性来关闭基础流,但是我没有在Microsoft工作,所以他们按照我的想法进行:D
Aaron Murgatroyd
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.