MemoryStream.Close() 或 MemoryStream.Dispose()

98

我该调用哪一个?

需要同时调用两个函数吗?

如果我已经调用其中一个,那么另一个会抛出异常吗?


Dispose() 会处理一切。 - xandy
4
将你的使用包装在 using 中,不要担心它。使用以下代码:using (var s = new MemoryStream()) {
}参考此问题:https://dev59.com/f2ct5IYBdhLWcg3wmOjg
- Randy James
1
不要调用 Close()文档指出应该确保流被正确处理而不是调用它,而 MemoryStream 文档 明确表示不需要调用 Dispose(),因为它没有任何需要处理的资源。所以,在完成 MemoryStream 的使用后,就像其他托管类型一样将其留下,垃圾回收器会处理剩下的事情。 - saastn
10个回答

170

Close()Dispose()MemoryStream上的调用只有两个作用:

  • 标记该对象已被释放,以便将来对该对象的意外使用会引发异常。
  • 可能会释放对托管对象的引用,这可以使GC的工作更加容易,具体取决于GC实现的算法。 (在今天的GC算法中,这没有真正的区别,因此这是一个学术讨论的重点,对现实世界没有重大影响。)

MemoryStream没有任何未受控资源需要处理,因此您不必严格释放它。 不释放MemoryStream的效果与放弃对byte[]的引用相同-- GC将以相同的方式清理两者。

我应该调用哪一个?是否需要同时调用两者?

流的Dispose()方法直接委托给Close()方法2,因此两者完全相同。

如果我已经调用了其中一个,另一个会抛出异常吗?

IDisposable.Dispose()的文档明确说明可以多次调用Dispose(),对于任何对象都是安全的3。(如果对于某个类不是这样,那么该类实现了IDisposable接口,违反了其契约,这将是一个错误。)

所有这些都是为了说:是否释放MemoryStream并没有太大关系。它具有Close/Dispose方法的唯一真正原因是它继承自Stream,后者需要这些方法作为其合同的一部分,以支持具有未托管资源(如文件或套接字描述符)的流。


1 Mono的实现没有释放byte[]的引用。我不知道Microsoft的实现是否有。

2 "该方法调用Close,然后调用Stream.Dispose(Boolean)。"

3 "如果一个对象的Dispose方法被调用多次,则对象必须忽略第一次之后的所有调用。"


请注意,如果您某种原因持有对已处理的流对象的引用,则处理MemoryStream部分将使GC的工作更轻松。 - Edward Brey
@EdwardBrey 确实。这取决于GC的实现方式。今天所有流行的CLR实现都使用某种变体的标记-清除GC,所以这是一场学术讨论——今天的GC不会显示任何区别。 - cdhowie
6
关于你脚注1所提到的内容,根据.NET参考源代码,“.Dispose()”并不会释放内部的“byte[]”。它所设置为null的唯一资源是"_lastReadTask",而该变量仅在方法“Task<int> ReadAsync(byte[], int, int, CancellationToken)”中使用。除将该变量设置为null外,它所做的只是将"_isOpen"、"_writable"和"_expandable"设置为false。 - Scott Chamberlain

33

以上都不需要。你不需要调用CloseDispose

MemoryStream 不持有任何非托管资源,因此唯一需要回收的资源是内存,在您的代码不再引用 MemoryStream 对象时将在垃圾收集期间与其余的对象一起回收内存。

如果您有一个长期存在的 MemoryStream 引用,则可以将该引用设置为 null 以允许 MemoryStream 被垃圾收集。 CloseDispose 都不会释放流缓冲区或 MemoryStream 对象本身。

由于既没有 Stream 也没有 MemoryStream 的终结器,因此无需调用 CloseDispose 来导致调用 GC.SuppressFinalize 以优化垃圾回收。没有要压制的终结器。

MemoryStream 文档如下:

此类型实现 IDisposable 接口,但实际上没有任何需要处理的资源。这意味着不必通过直接调用 Dispose() 或使用语言结构(如 C# 中的 using 或 Visual Basic 中的 Using)来处理。


10

您可以使用using块来处理这个问题。当using块超出其作用域时,它会自动调用Dispose

示例:

using (MemoryStream ms = new MemoryStream())
{
    // Do something with ms..
}
// ms is disposed here
希望这有所帮助。

8

使用using块,以便在对象实现了IDisposable接口时进行处理。


6

我该叫哪一个?

任何一个都可以。

是否需要同时调用两个?

不需要,只需调用其中一个即可。

如果我已经调用了其中一个,另一个会抛出异常吗?

不会,一次性模式声明后续对Dispose的调用不会产生负面影响。


刚刚在一个实时应用程序中经历了这个问题。在CLR 4.5中,关闭和释放确实有所不同。Dispose 应该足够了,但是如果您愿意,可以先调用 Close,然后再调用 Dispose。 - rkralston

6
以下代码是Reflector中的Stream.Dispose,如您所见,如果使用Dispose(在使用Using时隐式),则无需关闭。
public void Dispose()
{
    this.Close();
}

4

调用Close()方法将在内部调用Dispose()方法来释放资源。

请参阅此链接获取更多信息: msdn


我认为你的意思是调用 Dispose() 会在内部调用 Close(),但反过来不行。 - Adilet Soronov

2
只调用 Dispose() 就可以解决问题 =)

2
在.NET 3.5中(未检查其他版本),释放MemoryStream时,按以下顺序调用方法:
1. Stream.Dispose() - 简单地调用Close 2. Stream.Close() - 调用Dispose(true),然后GC.SuppressFinalize(this)
3. MemoryStream.Dispose(true) - 将_isOpen、_writable和_expandable标志设置为false 4. Stream.Dispose(true) - 如果活动,则关闭异步事件

1
作为第一种解决方案,建议尽可能使用using语句。这在这里描述:http://msdn.microsoft.com/en-us/library/yh598w02.aspx 当IDisposable对象的生命周期仅限于单个方法时,应在using语句中声明和实例化它。using语句以正确的方式调用对象上的Dispose方法,并且(当您像前面所示地使用它时),它还会在Dispose被调用后立即使对象本身超出范围。在using块内,对象是只读的,无法修改或重新分配。
现在回到问题,正如其他人建议的,在大多数.NET框架类中,Close()和Dispose()之间没有区别,而且无论您调用哪种方法都没有关系。您应该调用其中一个但不是两者都调用。然而,有例外情况。
有例外情况;例如,System.Windows.Forms.Form和System.Data.SqlClient.SqlConnection对Close()和Dispose()具有不同的行为。

完整的细节可以在此处查看:https://blogs.msdn.microsoft.com/kimhamil/2008/03/15/the-often-non-difference-between-close-and-dispose/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接