我可以相信一个对象在 C# 中超出范围时会被销毁并立即调用其析构函数吗?
我认为它应该是因为许多常见的编码实践(例如事务对象)都依赖于这种行为,但我不太习惯使用垃圾收集,并且对这些语言的通常行为方式知之甚少。
谢谢。
我可以相信一个对象在 C# 中超出范围时会被销毁并立即调用其析构函数吗?
我认为它应该是因为许多常见的编码实践(例如事务对象)都依赖于这种行为,但我不太习惯使用垃圾收集,并且对这些语言的通常行为方式知之甚少。
谢谢。
不,.Net 和因此 C# 依赖于垃圾收集内存管理。所以析构函数(在 .Net 中称为终结器)在 GC 发现可以销毁对象之前不会被调用。
另外:C# 中的大多数“常规”对象没有析构函数。如果您需要析构函数模式,您应该使用Dispose Pattern实现IDisposable 接口。在一次性对象上,您还应该确保使用 using 关键字或直接调用方法调用 Dispose 方法。
进一步(希望)澄清:确定性处置在 .Net 中很有用,例如,当您需要显式释放不受 .Net 运行时管理的资源时。此类资源的示例是文件句柄、数据库连接等。这些资源在不再需要时立即释放通常很重要。因此,我们不能等待 GC 释放它们。
为了在 .Net GC 的非确定性世界中获得确定性处置(类似于 C++ 的作用域行为),.Net 类依赖于 IDisposable 接口。借用Dispose Pattern,这里有一些例子:
首先,实例化一次性资源,然后让对象超出范围,这将由 GC 来处理对象:
1. {
2. var dr = new DisposableResource();
3. }
为了解决这个问题,我们可以显式地处理对象:
1. {
2. var dr = new DisposableResource();
3.
4. ...
5.
6. dr.Dispose();
7. }
但是如果第 2 行和第 6 行之间出现问题怎么办?不会调用 Dispose。为了进一步确保无论任何异常情况最终都会调用 Dispose,我们可以执行以下操作:
1. var dr = new DisposableResource();
2. try
3. {
4. ...
5. }
6. finally
7. {
8. dr.Dispose();
9. }
由于经常需要这种模式,C# 包含 using 关键字来简化事情。下面的例子等价于上面的例子:
1. using (var dr = new DisposableResource())
2. {
3. ...
4. }
不。一个对象实际上并没有“超出范围”,而是对它的引用(即你用来访问它的变量)。
一旦不再有对给定对象的引用,该对象就可以在需要时进行垃圾回收 (GC) 。每当 GC 决定它需要回收您不再引用的对象的空间时,就会调用对象终结器。
如果您的对象是一个资源(例如文件句柄、数据库连接),它应该实现 IDisposable 接口(它要求对象实现一种Dispose()
方法来清理任何打开的连接等)。在这种情况下,您的最佳实践是将对象创建为using
块的一部分,这样当该块完成时,您的应用程序将自动调用 objectsDispose()
方法,该方法将负责关闭您的文件/数据库连接/其他.
例如
using (var conn = new DbConnection())
{
// do stuff with conn
} // conn.Dispose() is automatically called here.
该using
块只是一些语法糖,它将您与conn
对象的交互包装在一个try
块中,以及一个finally
只调用conn.Dispose()
C# 中没有类似 C++ 的析构函数。(C# 中有一个不同的析构函数概念,也称为终结器,它使用与 C++ 析构函数相同的语法,但它们与销毁对象无关。它们旨在为非托管资源提供清理机制。)垃圾收集器将在不再引用对象后的某个时间清理对象。不是立即的,也没有办法保证这一点。
幸运的是,您也没有真正的理由要保证这一点。如果你需要内存,那么 GC 会回收它。如果你不这样做,为什么要关心周围是否还有一些垃圾?这不是内存泄漏:GC 仍然可以找到它并随时清理它。
不,这不能保证。与 Java 等语言类似,在 C# 中,垃圾收集器在需要时运行(即当堆太满时)。但是,当您的对象实现时IDisposable
,i。e. 他们有一个Dispose()
方法,必须调用它,然后你可以利用using
关键字:
using (var foo = new DisposableObject()) {
// do something with that
}
离开该块Dispose()
时将立即调用该方式。using
注意:IDisposable
在许多类型中都可以找到,最显着的是 GDI+,还有数据库连接、事务等,因此这里可能确实是正确的模式。
注意 2:在幕后,上面的块将被翻译成try
/finally
块:
var foo = new DisposableObject();
try
{
// do something with that
}
finally
{
foo.Dispose();
}
但是这种翻译是由编译器完成的,非常方便不要忘记调用Dispose()
.
我认为您不应该以这种方式依赖垃圾收集器。即使您扣除他们的操作方式,也很可能在下一个版本中他们重新实现了它。
在任何情况下,对象在您取消引用它们的那一刻都不会被垃圾收集。通常,它们会被收集,直到达到某个阈值,然后才被释放。
尤其是在 Java 程序中,当您查看任务管理器上的内存消耗时,这一点非常明显。它不断地增长,突然之间每一分钟它都会再次下降。
否。如果您参考 CLI 规范(关于终结器的第 8.9.6.7 页)http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf,您可以找到以下内容
CLI应确保在实例变得不可访问后立即调用终结器。虽然依靠内存压力来触发终结是可以接受的,但实施者应该考虑使用额外的指标
但它不能。