Stream.Dispose 是不是总是调用 Stream.Close(和 Stream.Flush)

Posted

技术标签:

【中文标题】Stream.Dispose 是不是总是调用 Stream.Close(和 Stream.Flush)【英文标题】:Does Stream.Dispose always call Stream.Close (and Stream.Flush)Stream.Dispose 是否总是调用 Stream.Close(和 Stream.Flush) 【发布时间】:2010-10-29 00:36:29 【问题描述】:

如果我有以下情况:

StreamWriter MySW = null;
try

   Stream MyStream = new FileStream("asdf.txt");
   MySW = new StreamWriter(MyStream);
   MySW.Write("blah");

finally

   if (MySW != null)
   
      MySW.Flush();
      MySW.Close();
      MySW.Dispose();
   

我可以直接致电MySW.Dispose() 并跳过关闭,即使它已提供?是否有任何未按预期工作的 Stream 实现(如 CryptoStream)?

如果不是,那么下面的代码就是不好的代码:

using (StreamWriter MySW = new StreamWriter(MyStream))

   MySW.Write("Blah");

【问题讨论】:

为什么要大写局部变量?它伤害了我可怜的头:( 我的惯例是使用大写的本地作用域,并使用较低的参数(NewOrderLineItem 与 newOrderLineItem)。正是我习惯的 =) Close and Dispose - which to call?的可能重复 @BinaryWorrier,另一个问题更通用,涵盖了许多不同的情况。 SqlConnection 展示了 CloseDispose 之间的一些差异,而 Stream 没有。 【参考方案1】:

我可以直接调用 MySW.Dispose() 和 跳过关闭,即使它是 提供?

是的,这就是它的用途。

是否有任何 Stream 实现 不能按预期工作(比如 加密流)?

可以安全地假设,如果一个对象实现了IDisposable,它会正确地处理自己。

如果没有,那将是一个错误。

如果不是,那么下面的就是不好的 代码:

不,该代码是处理实现 IDisposable 的对象的推荐方法。

更多优秀信息在Close and Dispose - which to call?接受的答案中

【讨论】:

【参考方案2】:

我使用Reflector,发现System.IO.Stream.Dispose是这样的:

public void Dispose()

    this.Close();

【讨论】:

【参考方案3】:

Stream.Close 是通过调用Stream.Dispose 来实现的,反之亦然——所以这些方法是等价的。 Stream.Close 的存在只是因为关闭流听起来比处理流更自然。

此外,您应该尽量避免显式调用此方法并改用using 语句,以便免费获得正确的异常处理。

【讨论】:

【参考方案4】:

StreamWriter.Dispose() 和 Stream.Dispose() 都释放对象持有的所有资源。它们都关闭了底层流。

Stream.Dispose()的源码(注意这是实现细节,不要依赖):

public void Dispose()

    this.Close();

StreamWriter.Dispose()(与 Stream.Dispose() 相同):

protected override void Dispose(bool disposing)

    try
    
        // Not relevant things
    
    finally
    
        if (this.Closable && (this.stream != null))
        
            try
            
                if (disposing)
                
                    this.stream.Close();
                
            
            finally
            
                // Not relevant things
            
        
    

不过,我通常会在处理流/streamwriters 之前隐式关闭它们 - 我认为它看起来更干净。

【讨论】:

【参考方案5】:

所有标准流(FileStream、CryptoStream)都将在关闭/处置时尝试刷新。我认为您可以将其用于任何 Microsoft 流实现。

因此,如果刷新失败,Close/Dispose 会抛出异常。

事实上,IIRC 在 FileStream 的 .NET 1.0 实现中存在一个错误,即如果刷新引发异常,它将无法释放文件句柄。这在 .NET 1.1 中已通过向 Dispose(boolean) 方法添加 try/finally 块来解决。

【讨论】:

【参考方案6】:

正如 Daniel Bruckner 所说,Dispose 和 Close 实际上是一回事。

但是 Stream 在处理/关闭时不会调用 Flush()。 FileStream (我假设任何其他具有缓存机制的 Stream )在处置时确实调用了 Flush() 。

如果您正在扩展 Stream 或 MemoryStream 等,则需要在必要时在处置/关闭时实现对 Flush() 的调用。

【讨论】:

我最近正在研究这个,如果你正在扩展 MemoryStream,你可能不需要担心刷新。查看 Flush for MemoryStream 的实现表明它实际上是一个空操作,实际上文档声明“重写 Flush 以便不执行任何操作。”。 @Mike,如果流直接写入内存而没有缓冲,则不需要刷新,如果流在其他未缓冲的流类型之上引入一些缓冲,则需要调用刷新. 对此无可争辩 - 抱歉挖掘了一个 2 年前的帖子! :)【参考方案7】:

对于需要手动关闭的对象,应尽一切努力在 using 块中创建对象。

//Cannot access 'stream'
using (FileStream stream = File.Open ("c:\\test.bin"))

   //Do work on 'stream'
 // 'stream' is closed and disposed of even if there is an exception escaping this block
// Cannot access 'stream' 

通过这种方式,人们永远不会在 using 子句的上下文之外错误地访问“流”,并且文件总是被关闭。

【讨论】:

【参考方案8】:

我查看了 Stream 类的 .net 源代码,它具有以下内容,这表明是的,您可以...

    // Stream used to require that all cleanup logic went into Close(),
    // which was thought up before we invented IDisposable.  However, we 
    // need to follow the IDisposable pattern so that users can write
    // sensible subclasses without needing to inspect all their base
    // classes, and without worrying about version brittleness, from a
    // base class switching to the Dispose pattern.  We're moving 
    // Stream to the Dispose(bool) pattern - that's where all subclasses
    // should put their cleanup starting in V2. 
    public virtual void Close() 
    
        Dispose(true); 
        GC.SuppressFinalize(this);
    

    public void Dispose() 
    
        Close(); 
     

【讨论】:

以上是关于Stream.Dispose 是不是总是调用 Stream.Close(和 Stream.Flush)的主要内容,如果未能解决你的问题,请参考以下文章

STM32F407板子总是重启

setPowerState 是不是总是在启动后调用?

拆除 UIViewController 时是不是总是调用 viewDidUnload 和 dealloc?

CoInitialize() 是不是总是在每个 .Net Framework / .Net Core 线程上隐式调用?

为啥 INVOKE 总是取消引用数据成员而不是尽可能地调用?

AppDelegate 中的应用程序 didFinishLaunching 是不是总是在更新后打开应用程序后被调用?