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


151

类如StreamStreamReaderStreamWriter等工具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();
   }
}

如您所见,我已经编写了using()构造,该构造自动Dispose()在每个对象上调用方法。但是我也调用Close()方法。这样对吗?

请向我建议使用流对象时的最佳做法。:-)

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

好吗?


如果您使用的是ReSharper,则可以在模式目录中将其定义为“反模式”。ReSharper将针对您的定义将每种用法标记为错误/提示/警告。也可以定义ReSharper对于这种情况必须如何应用QuickFix。
托尔斯滕·汉斯

3
提示:您可以将using语句用于多个一次性itens:使用(Stream responseStream = response.GetResponseStream())使用(StreamReader reader = new StreamReader(responseStream))使用(StreamWriter writer = new StreamWriter(filename)) {// ...某些代码}
Latrova 2014年


您不需要像嵌套嵌套using语句那样将它们堆叠在一起,并具有一组括号。在另一篇文章中,我建议的代码片段应使用与该技术的陈述,如果你想看看和修复你的“代码箭头”有一个编辑:stackoverflow.com/questions/5282999/...
蒂莫西·冈萨雷斯

2
@ Suncat2000您可以有多个using语句,但不能嵌套它们,而要堆叠它们。我的意思不是这样的语法,它限制了类型:using (MemoryStream ms1 = new MemoryStream(), ms2 = new MemoryStream()) { }。我的意思是这样,您可以在其中重新定义类型:using (MemoryStream ms = new MemoryStream()) using (FileStream fs = File.OpenRead("c:\\file.txt")) { }
Timothy Gonzalez

Answers:


101

快速跳转到Reflector.NET可以看到打开的Close()方法StreamWriter是:

public override void Close()
{
    this.Dispose(true);
    GC.SuppressFinalize(this);
}

并且StreamReader是:

public override void Close()
{
    this.Dispose(true);
}

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);
        }
    }
}

StreamWriter方法是相似的。

因此,阅读代码很明显,您可以随意随意调用Close()Dispose()在流上进行调用,并且可以按任意顺序进行。它不会以任何方式改变行为。

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

我个人的喜好是,using ( ... ) { ... }应尽可能始终使用它,因为它可以帮助您“不用剪刀剪”。

但是,尽管这有助于正确性,但确实会降低可读性。在C#中,我们已经有过多的右花括号,因此我们如何知道哪个实际上对流执行了大括号?

所以我认为最好这样做:

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

    stream.Close();
}

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


20
在C#中,我们已经有很多关闭花括号的功能,因此我们如何知道哪个实际上对流执行了关闭操作? ”我不认为这是个大问题:流“在正确的时间”关闭了,即,当变量超出范围并且不再需要时。
Heinzi 2011年

110
嗯,不,那是“为什么要把它关两次?” 阅读时减速。
汉斯·帕桑

57
我不同意多余的Close()电话。如果经验不足的人看了一下代码却不知道using他会做的:1)查找并学习,或者2)盲目添加Close()手动代码。如果他选择2),也许其他开发人员会看到多余的内容Close(),而不是“咯咯笑”,而是指示经验不足的开发人员。我不赞成使经验不足的开发人员生活困难,但我赞成将其转变为经验丰富的开发人员。
R. Martinho Fernandes

14
如果使用+ Close()并打开/ analyze,则会收到“警告:CA2202:Microsoft。用法:对象'f'可以在方法'Foo(string)'中多次处置。以避免生成系统。 ObjectDisposedException,您不应在一个对象上多次调用Dispose 。:行:41“因此,根据文档和/ analyze的说法,虽然当前实现可以调用Close和Dispose很好,但是这样做并不可行,并且可能会在将来的版本中进行更改。净。
marc40000 '02

4
+1为好答案。另一件事要考虑。为什么不在/// Close之类的右括号后添加注释,或者像我这样,作为一个新手,我在不清楚的任何右括号后添加一个衬纸。例如在一个长类中,我将在最后一个结束大括号之后添加// End Namespace XXX,并在第二个最后一个结束大括号之后添加// End Class YYY。这不是评论的意思吗?只是好奇。:)作为一个新手,我看到了这样的代码,思考为什么要来这里。我确实问了一个问题:“为什么需要第二次关闭”。我觉得多余的代码行并没有增加清晰度。抱歉。
弗朗西斯·罗杰斯

51

不,您不应该手动调用这些方法。在该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);
                 }
            }
         }

Close()方法调用Dispose()


1
我很确定您不需要是using第一个,responseStream因为它由包裹,reader这将确保在处置阅读器时将其关闭。不过+1
Isak Savo

当您说The Close method calls Dispose... 时,这令人困惑,并且在您的其余文章中,您暗示那Dispose()会打电话给Close()我,我不应该手动打电话给后者。你是说他们互相打电话吗?
Nawaz

@Nawaz,我的帖子令人困惑。Close方法只是调用Dispose。在您的情况下,您需要使用Dispose才能释放非托管资源。通过将代码包装在using语句中,调用Dispose方法。
Darin Dimitrov

3
糟糕的答案。假设您可以使用一个using块。我正在实现一个不时编写的类,因此无法编写。
2014年

5
@Jez然后,您的类应该实现IDisposable接口,如果close是该区域中的标准术语,则还应该实现Close(),以便使用您类的类可以使用using(或再次使用Dispose Pattern)。
Dorus 2015年

13

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

StreamReader.Close:Close的此实现调用传递真实值的Dispose方法。

StreamWriter.Close:Close的此实现调用传递真实值的Dispose方法。

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

因此,这两个都同样有效:

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

/* Option 2, explicitly calling Close */
StreamWriter writer = new StreamWriter(filename)
try {
    // do something
}
finally {
    writer.Close();
}

就我个人而言,我会坚持使用第一个选项,因为它包含的“噪音”更少。


5

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

的合同IDisposable.Dispose明确要求,在永远不会再次使用的对象上调用它在最坏的情况下是无害的,因此我建议在每个对象上调用IDisposable.Dispose或方法,无论是否也调用。Dispose()IDisposableClose()


仅供参考,这是MSDN博客上的一篇文章,介绍了“关闭和处置”的乐趣。blogs.msdn.com/b/kimhamil/archive/2008/03/15/...
JamieSee

1

这是一个古老的问题,但是您现在可以使用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);
    }
}

https://docs.microsoft.com/zh-CN/dotnet/csharp/language-reference/proposals/csharp-8.0/using

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.