有啥方法可以在不关闭其 BaseStream 的情况下关闭 StreamWriter?

Posted

技术标签:

【中文标题】有啥方法可以在不关闭其 BaseStream 的情况下关闭 StreamWriter?【英文标题】:Is there any way to close a StreamWriter without closing its BaseStream?有什么方法可以在不关闭其 BaseStream 的情况下关闭 StreamWriter? 【发布时间】:2011-02-09 15:32:17 【问题描述】:

我的根本问题是,当usingStreamWriter 上调用Dispose 时,它也会处理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;

【问题讨论】:

这是一个类似的问题:***.com/questions/2620851 我正在使用来自 WebRequest 的流来执行此操作,有趣的是,如果编码是 ASCII 而不是 UTF8,您可以关闭它。很奇怪。 tofutim,我已经将我的编码为 ASCII,它仍然处理底层流.. 【参考方案1】:

如果您使用的是 .NET Framework 4.5 或更高版本,则有一个StreamWriter overload using which you can ask the base stream to be left open when the writer is closed。

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

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

【讨论】:

显然 4.5 的重载是一个没有经过深思熟虑的让步——重载需要缓冲区大小,它不能为 0 也不能为空。在内部我知道 128 个字符是最小大小,所以我将其设置为 1。否则这个“功能”让我很高兴。 有没有办法在创建StreamWriter 之后设置leaveOpen 参数? @c00000fd:我不知道。 @Yepeekai:“如果我将流传递给子方法并且该子方法创建 StreamWriter,它将在该子方法执行结束时被释放”不,这根本不是真的.只有在有人调用Dispose 时才会处理它。方法结束不会自动执行此操作。如果它有一个终结器,它可能会在以后最终确定,但这不是一回事 - 仍然不清楚你预期的危险是什么。如果您认为从方法中返回 StreamWriter 是不安全的,因为它可能会被 GC 自动释放,那是不正确的。 @Yepeekai:IIRC,StreamWriter 没有终结器 - 正是出于这个原因,我不希望它有。【参考方案2】:

.NET 4.5 有一个新方法:

StreamWriter(Stream, Encoding, Int32, Boolean)

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

【讨论】:

谢谢伙计!不知道这一点,如果有什么是我开始瞄准 .NET 4.5 的好理由! 遗憾的是,没有不需要设置 bufferSize 的重载。我对那里的默认设置很满意。我必须自己通过。不是世界末日。 默认的bufferSize1024。 Details are here.【参考方案3】:

内存流有一个 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 在被释放后不能被调用。【参考方案4】:

你需要创建 StreamWriter 的后代并重写它的 dispose 方法,通过始终将 false 传递给 disposing 参数,它将强制流写入器不关闭,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);
    

【讨论】:

我相信这不会像您认为的那样。 disposing 标志是the IDisposable pattern 的一部分。始终将false 传递给基类的Dispose(bool) 方法基本上向StreamWriter 发出信号,表明它正在从终结器调用(当您显式调用Dispose() 时并非如此),因此不应访问任何托管对象。 是它不会处理基本流的原因。但是,您实现这一目标的方式是一种技巧。一开始就不叫Dispose会简单得多! 这就是赛门铁克,除了完全从头开始重写流媒体之外,您所做的任何事情都将是一种黑客行为。当然,您可以根本不调用 base.Dispose(false),但不会有功能差异,我喜欢我的示例的清晰性。但是,请记住这一点,StreamWriter 类的未来版本可能不仅仅是在处理流时关闭流,因此调用 dispose(false) 也可以证明它。但对每个人来说都是他自己的。 另一种方法是创建自己的流包装器,其中包含另一个流,其中 Close 方法不执行任何操作而不是关闭底层流,这不是一个 hack,而是一个更多的工作. 惊人的时机:我正要建议同样的事情(装饰类,可能命名为OwnedStream,忽略Dispose(bool)Close)。 是的,上面的代码是我如何实现快速而肮脏的方法,但如果我正在制作商业应用程序或对我来说真正重要的东西,我会使用 Stream 包装器类正确地完成它.就我个人而言,我认为微软在这里犯了一个错误,streamwriter 应该有一个布尔属性来关闭底层流,但是我不在微软工作,所以我猜他们会做他们喜欢做的事情:D【参考方案5】:

不要在StreamWriter 上拨打Dispose。这个类是一次性的原因不是因为它拥有非托管资源,而是允许处理本身可以拥有非托管资源的流。如果底层流的生命周期在其他地方处理,则无需处置 writer。

【讨论】:

@Marc,不会调用Flush 来完成这项工作,以防它缓冲数据? 很好,但是一旦我们退出 CreateStream,StreamWrtier 是可收集的,迫使第三方阅读器与 GC 竞争,这不是我想要留在的情况。或者我错过了什么? @BinaryWorrier:不,没有竞争条件:StreamWriter 没有终结器(实际上不应该)。 @Binary Worrier:如果你直接拥有资源,你应该只有一个终结器。在这种情况下,StreamWriter 应该假设 Stream 会在需要时自行清理。 StreamWriter 的 'close' 方法似乎也关闭并处理了流。因此,必须刷新,但不能关闭或处置流写入器,因此它不会关闭流,这相当于处置流。这里的 API 提供了太多“帮助”。

以上是关于有啥方法可以在不关闭其 BaseStream 的情况下关闭 StreamWriter?的主要内容,如果未能解决你的问题,请参考以下文章

使用图像映射生成器有啥好的替代方法? [关闭]

有啥方法可以在不重新加载的情况下更改标头 URL? [复制]

有啥方法可以在不使用 getImageData() 的情况下在 JavaScript 中找到像素的颜色?

有啥方法可以在不将 CIImage 转换为 UIImage 或 CGImage 的情况下将 CIImage 转换为 NSData?

有啥方法可以在不使用 XMPP 协议的情况下连接到 Facebook 聊天服务?

有啥方法可以在不使用 IDFA 的情况下为设备生成唯一 ID? [复制]