将对象设置为null与Dispose()

我对CLR和GC的工作方式非常着迷(我正在通过C#阅读CLR,Jon Skeet的书籍/文章等来扩展我的知识)。

无论如何,有什么区别说:

MyClass myclass = new MyClass(); myclass = null; 

或者,通过使MyClass实现IDisposable和析构函数并调用Dispose()?

另外,如果我有一个使用using语句的代码块(例如下面的代码),如果我单步执行代码并退出using块,那么是抛出的对象还是垃圾回收? 如果我在use块中调用Dispose(),会发生什么?

 using (MyDisposableObj mydispobj = new MyDisposableObj()) { } 

stream类(例如BinaryWriter)有一个Finalize方法? 我为什么要使用它?

将垃圾收集与处理分开是很重要的。 他们是完全分开的东西,有一个共同点,我会在一分钟之内来​​。

Dispose ,垃圾收集和定稿

当你写一个using语句时,它只是try / finally块的语法糖,所以即使在using语句的主体中的代码抛出一个exception时,也会调用Dispose 。 这并不意味着该对象在块的末尾被垃圾收集。

处置是关于非托pipe资源 (非内存资源)。 这些可能是UI手柄,networking连接,文件句柄等。这些资源是有限的,所以你通常希望尽快释放它们。 每当你的types“拥有”一个非托pipe资源时,你应该实现IDisposable直接(通常通过IntPtr )或间接(例如通过StreamSqlConnection等)。

垃圾收集本身只是关于记忆 – 有一点点转折。 垃圾收集器能够find不能再被引用的对象,并释放它们。 它不会一直寻找垃圾 – 只有当它检测到它需要(例如,如果堆的一代“内存不足”)。

扭曲正在敲定 。 垃圾回收器保存了一个不再可达的对象列表,但是有一个终结器(在C# ~Foo()写成,有些令人困惑 – 它们不像C ++析构函数)。 它在这些对象上运行终结器,以防他们的内存被释放之前需要额外的清理。

在types的用户已经忘记处理有序的情况下,终结器几乎总是用于清理资源。 所以,如果你打开一个FileStream但忘记调用DisposeClose ,终结器将最终为你释放底层的文件句柄。 在一个写得很好的程序中,终结者应该几乎不会在我看来开火。

设置一个variables为null

将variables设置为null一个小点 – 为了垃圾收集,这几乎不需要。 如果它是一个成员variables,你可能有时想这样做,虽然根据我的经验,不再需要一个对象的“部分”。 当它是一个局部variables时,JIT通常足够聪明(在释放模式下)以知道何时不再使用引用。 例如:

 StringBuilder sb = new StringBuilder(); sb.Append("Foo"); string x = sb.ToString(); // The string and StringBuilder are already eligible // for garbage collection here! int y = 10; DoSomething(y); // These aren't helping at all! x = null; sb = null; // Assume that x and sb aren't used here 

有一点值得把局部variables设置为null ,当你在循环中时,循环的一些分支需要使用这个variables,但是你知道你已经达到了一个你不知道的地步。 例如:

 SomeObject foo = new SomeObject(); for (int i=0; i < 100000; i++) { if (i == 5) { foo.DoSomething(); // We're not going to need it again, but the JIT // wouldn't spot that foo = null; } else { // Some other code } } 

实现IDisposable /终结器

那么,你自己的types应该实现终结者? 几乎肯定不是。 如果你只间接地持有非托pipe资源(例如你有一个FileStream作为一个成员variables),那么添加你自己的终结器将无济于事:当你的对象是stream时,这个stream几乎肯定有资格进行垃圾回收,所以你可以依靠在FileStream有一个终结器(如果需要的话 – 它可能会引用其他东西等)。 如果你想直接持有非托pipe资源, SafeHandle是你的朋友,需要一点时间才能继续下去,但这意味着你几乎 不需要再写一个终结器 。 你通常只需要一个终结器,如果你有一个真正的直接处理资源(一个IntPtr ),你应该看看尽快移动到SafeHandle 。 (有两个链接 – 理想地阅读两个。)

Joe Duffy 在终结者和IDisposable (与许多聪明人合写)方面有很长一段时间 ,值得一读。 值得注意的是,如果你封闭你的类,它会让生活变得更容易:重写Dispose方式调用一个新的虚拟Dispose(bool)方法等等,只有当你的类被devise用于inheritance的时候。

这有点漫不经心,但请澄清你想在哪里:)

当你处理一个对象时,资源就被释放了。 当你给一个variables赋值null时,你只是改变一个引用。

 myclass = null; 

执行完这个之后,myclass所指向的对象仍然存在,并且会一直持续到GC开始清理。 如果Dispose被显式调用,或者处于使用块中,则会尽快释放所有资源。

这两个行动并没有太大的关系。 当你将一个引用设置为null时,它就是这样做的。 它本身并不影响被引用的类。 您的variables不再指向过去的对象,但对象本身不变。

当你调用Dispose()时,它是对象本身的一个方法调用。 无论Dispose方法如何,现在都在对象上完成。 但是这并不影响你对这个对象的引用。

唯一重叠的地方是没有更多的对象引用时,它最终会被垃圾收集。 如果类实现了IDisposable接口,那么Dispose()将在垃圾收集之前在对象上被调用。

但是,在将您的引用设置为null之后不会立即发生,原因有二。 首先,其他的引用可能存在,所以它根本不会被垃圾回收,其次,即使这是最后一个引用,所以现在已经准备好垃圾回收了,直到垃圾回收器决定删除物体。

在对象上调用Dispose()不会以任何方式“杀死”对象。 它通常被用来清理,以便之后可以安全地删除对象,但最终,Dispose没有什么神奇的,它只是一个类方法。