如果您在处置之前复制其参考资料,IDisposable是否仍会被处置?

时间:2014-05-28 02:03:33

标签: c# .net garbage-collection idisposable

考虑一下:

interface IFoo : IDisposable { }

class Program
{
  static void Main()
  {
    var foo = GetFoo();

    var anotherFoo = foo;

    using(anotherFoo)
    {
    }

    // Will the object on the heap be marked for collection? 
    // Or will this confuse the garbage collector 
    // as we are copying references?
  }
}

这提出了更重要的问题。 Dispose()实际上做了什么?

3 个答案:

答案 0 :(得分:4)

考虑到托管内存以外的资源(非托管资源)仍然需要明确释放的事实,添加了

Dispose方法; GC特别是设计。

此外,IDisposable背后的机制与GC无关。当您按照Dispose Pattern进行操作时,您可以选择将您的代码插入GC,方法是让您的课程最终确定,但在实施IDisposable时您不必这样做。

Disposing与清理对象所拥有的隐藏资源有关,而不是清理托管内存中对象的“shell”。在调用ObjectDisposedException之后,对象会经常Dispose尝试使用它。

回到你的代码,由于这两个变量实际上引用了同一个对象,foo变量将在using块之后引用一个被处置的对象。因此,在foo之后调用方法或访问using的属性可能会引发异常:

var foo = GetFoo();

var anotherFoo = foo;

using(anotherFoo)
{
}

foo.doSomethingUseful(); // <<== This may throw ObjectDisposedException!

答案 1 :(得分:3)

&#34;复制参考文献&#34;正如您所描述的那样,不会对原始对象执行任何。它只是通过不同名称查找同一对象的另一种方式 - 例如,如果您有一个单独的电子邮件地址,其邮件始终会发送到您的邮箱。无论哪种方式,你到达同一个对象。因此,如果您处置一个,那么您将处置另一个。

当你有代码时:

using (anotherFoo) 
{
}

您正在使用using语法来确保在块完成之前发生的最后一件事是变量{{对象引用.Dispose方法1}}将被处置。因此,它与您完成anotherFoo完全相同。

编辑回复评论

首先,最重要的是,using (foo) { ... } nothing 与垃圾收集有关。这种情况本身就是发生的。在此示例中,由于IDisposablefoo都是局部变量,并且(显然)不是通过闭包,方法调用等从方法中转义出来,因此它们几乎就会被GC编辑。方法调用结束。

另一方面,

anotherFoo纯粹是为了允许你释放(通常)外部资源,如文件,UI原语,套接字等操作系统句柄。虽然你是真的肯定希望对象在GC发布时处理它的资源,没有IDisposable你无法控制反向 - { {1}}允许您在不再使用资源时清理资源。这使您可以为处理外部资源的问题设计更具确定性的解决方案 - 而不仅仅是依靠GC允许的内容。

答案 2 :(得分:1)

将任何引用类型的每个变量,参数或其他存储位置视为持有“Object#291”形式的标记[但具有不同的数字],并认为系统具有一些神奇的神秘能力来定位任何对象给出该表单的标记,并访问其字段或告诉它做某事。在某事上致电Dispose告诉它“您的服务不再需要。一旦您从这种方法返回,您可能会在任何时候被放弃而不另行通知。按照您的意愿做好准备。”正常的期望是,如果对象在宇宙中的任何地方询问任何外部实体,为了代表它做某事,该对象将告诉外部实体它不再需要这样做。

假设对象#291是File。它已要求操作系统授予它对磁盘上文件的独占访问权限,操作系统通过返回文件句柄来响应该请求;没有那个句柄,没有人可以访问该文件。在对象#291上调用Dispose将指示它告诉操作系统它不再需要访问该文件,并且它将不再出于任何目的使用该句柄。一旦发生这种情况,对象#291将无法再访问磁盘上的文件,任何从对象#291读取数据的尝试都将失败。

请注意 - 除了一个例外 - Object#291的行为不会受到存在的引用数量的影响。引用数量唯一重要的时间是“强根参考”的数量降至零。当发生这种情况时,除非已请求放弃通知,否则该对象将不再存在。如果一个对象请求了这样的通知(通过实现一个名为Finalize的方法),那么第一次发现它被放弃时,系统将确保它被复活的时间足够长,以便在它停止之前运行其Finalize方法存在。