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

类如 StreamStreamReaderStreamWriter等实现了 IDisposable接口。这意味着,我们可以对这些类的对象调用 Dispose()方法。他们还定义了一个名为 Close()public方法。这让我很困惑,在处理完对象之后我应该调用什么?如果我两个都打呢?

我现在的代码是这样的:

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()方法:

好吃吗?

169732 次浏览

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

快速跳转到 Refector.NET 显示 StreamWriter上的 Close()方法是:

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

StreamReader是:

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

StreamReader中的 Dispose(bool disposing)覆盖是:

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

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

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

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

关闭 : 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();
}

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

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

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

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

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://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using

值得一提的是,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.

简而言之,Close之所以存在是因为它早于 Dispose,并且由于兼容性原因不能删除它。