当前位置:  开发笔记 > 编程语言 > 正文

将对象设置为null vs Dispose()

如何解决《将对象设置为nullvsDispose()》经验,为你挑选了3个好方法。

我对CLR和GC的工作方式很着迷(我正在通过C#,Jon Skeet的书籍/帖子等阅读CLR来扩展我的知识).

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

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

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

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

using (MyDisposableObj mydispobj = new MyDisposableObj())
{

}

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



1> Jon Skeet..:

将处理与垃圾收集分开是很重要的.它们是完全不同的东西,有一个共同点我将在一分钟内到达.

Dispose,垃圾收集和定稿

当你编写一个using语句时,它只是try/finally块的语法糖,因此Dispose即使using语句正文中的代码抛出异常,也会调用它.这并不意味着该对象在块的末尾被垃圾收集.

处置是关于非托管资源(非内存资源).这些可能是UI句柄,网络连接,文件句柄等.这些是有限的资源,因此您通常希望尽快释放它们.IDisposable只要您的类型"拥有"非托管资源,您应该直接(通常通过一个IntPtr)或间接(例如通过a Stream,a SqlConnection等)实现.

垃圾收集本身只是关于内存 - 只有一点点扭曲.垃圾收集器能够找到无法再引用的对象,并释放它们.它不会一直寻找垃圾 - 只有当它检测到它需要时(例如,如果堆的一个"代"耗尽内存).

扭曲是最终确定.垃圾收集器保存一个不再可访问的对象列表,但它们有一个终结器(用~Foo()C#编写,有点令人困惑 - 它们与C++析构函数完全不同).它在这些对象上运行终结器,以防它们需要在释放内存之前进行额外的清理.

在该类型的用户忘记以有序方式处理它的情况下,终结器几乎总是用于清理资源.因此,如果您打开FileStream但忘记调用DisposeClose,终结器最终将为您释放基础文件句柄.在一个写得很好的程序中,终结者几乎不应该在我看来.

将变量设置为 null

设置变量的一个小问题null- 为了垃圾收集,这几乎不需要.如果它是一个成员变量,你有时可能想要这样做,尽管根据我的经验,不再需要一个对象的"部分".当它是一个局部变量时,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

一个时间它可能是值得设置一个局部变量null是当你在一个循环,循环的一些分支机构需要使用变量,但你知道你已经达到在你做的不是一个点.例如:

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/finalizers

那么,你自己的类型应该实现终结器吗?几乎肯定不是.如果你只间接持有非托管资源(例如你有FileStream一个成员变量),那么添加你自己的终结器将无济于事:当你的对象出现时,流几乎肯定有资格进行垃圾收集,所以你可以依赖FileStream有一个终结者(如果有必要 - 它可能会引用别的东西,等等).如果你想"近乎"直接拥有一个非托管资源,那么SafeHandle你的朋友 - 它需要一些时间才能继续使用,但这意味着你几乎 不需要再次编写终结器.如果你对资源有一个非常直接的处理(a IntPtr),你通常只需要一个终结器,你应该转向SafeHandle你尽快做.(有两个链接 - 理想情况下读取两个.)

Joe Duffy 有一套很长的关于终结者和IDisposable的指导方针(与很多聪明的人合写)值得一读.值得注意的是,如果你密封你的类,它会让生活变得更容易:当你的类被设计为继承时,覆盖Dispose调用新的虚Dispose(bool)方法等的模式才有意义.

这有点絮絮叨叨,但请你澄清一下你想要的地方:)



2> recursive..:

处置对象时,将释放资源.为变量赋值null时,您只需更改引用.

myclass = null;

执行此操作后,myclass所指的对象仍然存在,并将继续执行直到GC开始清理它.如果显式调用Dispose,或者它在using块中,则将尽快释放所有资源.


它*可能*在执行该行后仍然不存在 - 它可能在该行之前被垃圾收集*.JIT是聪明的 - 这样的线条几乎总是无关紧要.
设置为null可能意味着对象持有的资源*永远不会被释放.GC没有处理,它只是最终确定,所以如果对象直接保存非托管资源并且其终结器没有处理(或者它没有终结器),那么这些资源将泄漏.需要注意的事情.

3> jalf..:

这两项行动彼此没有太大关系.当您将引用设置为null时,它只是这样做.它本身并不会影响所引用的类.您的变量不再指向它以前使用的对象,但对象本身不变.

当你调用Dispose()时,它是对象本身的方法调用.无论Dispose方法做什么,现在都在对象上完成.但这不会影响您对该对象的引用.

唯一的重叠区域是,不再有对象的引用时,它最终会被垃圾收集.如果类实现了IDisposable接口,那么在对象被垃圾收集之前将调用Dispose().

但是,在将引用设置为null之后,这不会立即发生,原因有两个.首先,可能存在其他引用,因此它根本不会收集垃圾,其次,即使这是最后一个引用,所以现在它已经准备好被垃圾收集,在垃圾收集器决定删除之前不会发生任何事情物体.

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

推荐阅读
殉情放开那只小兔子
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有