我应该为流对象调用Close()或Dispose()吗?

Naw*_*waz 139 c# idisposable stream streamwriter streamreader

类如Stream,StreamReader,StreamWriter等工具IDisposable界面.这意味着,我们可以Dispose()在这些类的对象上调用方法.他们还定义了一个public名为的方法Close().现在让我感到困惑的是,一旦我完成了对象,我该怎么称呼?如果我同时打电话怎么办?

我目前的代码是这样的:

using (Stream responseStream = response.GetResponseStream())
{
   using (StreamReader reader = new StreamReader(responseStream))
   {
      using (StreamWriter writer = new StreamWriter(filename))
      {
         int chunkSize = 1024;
         while (!reader.EndOfStream)
         {
            char[] buffer = new char[chunkSize];
            int count = reader.Read(buffer, 0, chunkSize);
            if (count != 0)
            {
               writer.Write(buffer, 0, count);
            }
         }
         writer.Close();
      }
      reader.Close();
   }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我编写了using()构造,自动调用Dispose()每个对象的方法.但我也称之为Close()方法.这样对吗?

请告诉我使用流对象时的最佳做法.:-)

MSDN示例不使用using()构造,并且调用Close()方法:

好吗?

Eni*_*ity 96

快速跳转到Reflector.NET显示该Close()方法StreamWriter是:

public override void Close()
{
    this.Dispose(true);
    GC.SuppressFinalize(this);
}
Run Code Online (Sandbox Code Playgroud)

并且StreamReader是:

public override void Close()
{
    this.Dispose(true);
}
Run Code Online (Sandbox Code Playgroud)

Dispose(bool disposing)超控StreamReader是:

protected override void Dispose(bool disposing)
{
    try
    {
        if ((this.Closable && disposing) && (this.stream != null))
        {
            this.stream.Close();
        }
    }
    finally
    {
        if (this.Closable && (this.stream != null))
        {
            this.stream = null;
            /* deleted for brevity */
            base.Dispose(disposing);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

StreamWriter方法是相似的.

因此,阅读代码很明显,您可以根据自己的喜好和任何顺序随时调用Close()&Dispose().它不会以任何方式改变行为.

所以它归结为它是否是更具可读性使用Dispose(),Close()和/或using ( ... ) { ... }.

我个人的偏好是using ( ... ) { ... }应尽可能使用,因为它可以帮助你"不用剪刀".

但是,虽然这有助于正确性,但确实会降低可读性.在C#中,我们已经有了大量的花括号,所以我们怎么知道哪一个实际上在流上执行了关闭呢?

所以我认为最好这样做:

using (var stream = ...)
{
    /* code */

    stream.Close();
}
Run Code Online (Sandbox Code Playgroud)

它不会影响代码的行为,但它确实有助于提高可读性.

  • 嗯,不,那是"为什么他要把它关闭两次?" 阅读时减速. (101认同)
  • 我不同意冗余的`Close()`调用.如果经验不足的人看了代码并且不知道"使用"他会:1)查找并**学习**,或2)盲目地手动添加`Close()`.如果他选择2),也许其他开发人员会看到多余的`Close()`而不是"轻笑",**指示**经验较少的开发人员.我并不赞成让没有经验的开发人员生活困难,但我赞成将他们变成经验丰富的开发人员. (52认同)
  • "*在C#中,我们已经有过多的结束花括号,所以我们怎么知道哪一个实际上在流上执行了关闭?*"我不认为这是一个大问题:流在关闭的时候关闭",即,当变量超出范围并且不再需要时. (19认同)
  • 如果使用+ Close()并打开/分析,则会出现"警告:CA2202:Microsoft.Usage:Object'f'可以在方法'Foo(string)'中多次处理.避免生成系统. ObjectDisposedException你不应该在一个对象上多次调用Dispose:Lines:41"因此,当调用Close和Dispose时,当前的实现很好,根据文档和/ analyze,它不好,可能会在以后的版本中更改.净. (12认同)
  • +1为好的答案.另外要考虑的事情.为什么不在结束括号之后添加评论,比如//关闭或者像我一样,作为一个新手,我在任何不清楚的右支撑之后添加一个衬垫.比如在一个很长的类中,我会在最后的结束括号后添加// End Namespace XXX,并在第二个最后的结束括号后添加// End Class YYY.这不是评论的意思.只是好奇.:)作为一个新手,我看到了这样的代码,为什么我来到这里.我确实问过"为什么需要第二次关闭".我觉得额外的代码行不会增加清晰度.抱歉. (4认同)
  • 虽然我很欣赏答案的细节,但我真的不喜欢依赖第三方功能的实施细节来决定"正确"的做事方式.如果Microsoft决定更改实现,那么无论您是调用Close还是Dispose(并按正确的顺序)都是重要的,那么当您的应用程序中断时您的错误是因为您依赖于实现细节而不是文档详细信息? (2认同)

Dar*_*rov 45

不,你不应该手动调用这些方法.在using块的末尾,自动调用Dispose方法,该方法将注意释放非托管资源(至少对于标准.NET BCL类,例如流,读取器/写入器......).所以你也可以这样写你的代码:

using (Stream responseStream = response.GetResponseStream())
    using (StreamReader reader = new StreamReader(responseStream))
        using (StreamWriter writer = new StreamWriter(filename))
        {
            int chunkSize = 1024;
            while (!reader.EndOfStream)
            {
                 char[] buffer = new char[chunkSize];
                 int count = reader.Read(buffer, 0, chunkSize);
                 if (count != 0)
                 {
                     writer.Write(buffer, 0, count);
                 }
            }
         }
Run Code Online (Sandbox Code Playgroud)

Close方法调用Dispose.

  • 可怕的答案.它假设您可以使用`using'块.我正在实现一个不时编写的类,因此不能. (3认同)
  • @Jez然后你的类应该实现IDisposable接口,也可能是Close()[如果close是该区域的标准术语](https://msdn.microsoft.com/en-us/library/b1yfkh5e%28v=vs. 110%29.aspx),这样使用你的类的类可以使用`using`(或者再次使用Dispose Pattern). (3认同)

Joe*_*nta 21

无论如何,源代码Stream.Close解释了为什么有两种方法:

// 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 now.
Run Code Online (Sandbox Code Playgroud)

简而言之,Close存在只是因为它早于Dispose,并且出于兼容性原因无法将其删除。


Hei*_*nzi 12

文档说这两种方法是等价的:

StreamReader.Close:Close的这个实现调用Dispose方法传递一个真值.

StreamWriter.Close:Close的这个实现调用Dispose方法传递一个真值.

Stream.Close:此方法调用Dispose,指定true以释放所有资源.

所以,这两个都同样有效:

/* Option 1 */
using (StreamWriter writer = new StreamWriter(filename)) { 
   // do something
} 

/* Option 2 */
StreamWriter writer = new StreamWriter(filename)
try {
    // do something
}
finally {
    writer.Close();
}
Run Code Online (Sandbox Code Playgroud)

就个人而言,我会坚持使用第一个选项,因为它包含较少的"噪音".


Tod*_*ton 8

这是一个老问题,但您现在可以编写 using 语句而无需阻止每个语句。当包含块完成时,它们将按相反的顺序处理。

using var responseStream = response.GetResponseStream();
using var reader = new StreamReader(responseStream);
using var writer = new StreamWriter(filename);

int chunkSize = 1024;
while (!reader.EndOfStream)
{
    char[] buffer = new char[chunkSize];
    int count = reader.Read(buffer, 0, chunkSize);
    if (count != 0)
    {
        writer.Write(buffer, 0, count);
    }
}
Run Code Online (Sandbox Code Playgroud)

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using


sup*_*cat 6

在许多同时支持Close()Dispose()方法的类上,这两个调用是等效的。但是,在某些类上,可以重新打开已关闭的对象。一些这样的类可能会在关闭后保持一些资源处于活动状态,以允许重新打开;其他人可能不会在 上保持任何资源处于活动状态Close(),但可能会设置一个标志Dispose()来明确禁止重新打开。

的契约IDisposable.Dispose明确要求在一个永远不会再次使用的对象上调用它在最坏的情况下是无害的,所以我建议在每个对象上调用一个IDisposable.Dispose或一个方法,无论Dispose()一个IDisposable对象是否也调用Close().