Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/305.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 垃圾收集器是否隐式使用析构函数方法,开发人员是否显式使用dispose方法来处理对象?_C#_Destructor_Dispose_Idisposable_Finalizer - Fatal编程技术网

C# 垃圾收集器是否隐式使用析构函数方法,开发人员是否显式使用dispose方法来处理对象?

C# 垃圾收集器是否隐式使用析构函数方法,开发人员是否显式使用dispose方法来处理对象?,c#,destructor,dispose,idisposable,finalizer,C#,Destructor,Dispose,Idisposable,Finalizer,我可以看到已经有很多关于dispose和destructor方法的线程,但我只想确保在继续之前正确理解它们 当对象不再被引用(即不再需要)时,垃圾回收器是否隐式使用析构函数方法?美国开发人员是否使用dispose方法显式处理垃圾回收器可能无法处理的对象 另外,我现在正在阅读所有这些,似乎这是一个或其他与这些方法的情况。例如,给出以下代码: class DansClass : IDisposable { public void Dispose() { GC.Supp

我可以看到已经有很多关于dispose和destructor方法的线程,但我只想确保在继续之前正确理解它们

当对象不再被引用(即不再需要)时,垃圾回收器是否隐式使用析构函数方法?美国开发人员是否使用dispose方法显式处理垃圾回收器可能无法处理的对象

另外,我现在正在阅读所有这些,似乎这是一个或其他与这些方法的情况。例如,给出以下代码:

class DansClass : IDisposable
{
    public void Dispose()
    {
        GC.SuppressFinalize(this);
        Console.WriteLine("Disposing...");
    }

    -DansClass()
    {
        Console.WriteLine("Destructing...");
    }
}
输出将是:

破坏…

可以理解,因为我们抑制了finalize(析构函数),所以只看到Dispose输出

但如果我注释掉SuppressFinalize()方法,则输出为:

处理…


为什么不调用析构函数呢?

您看到的行为是因为(您所指的析构函数在.Net中也称为终结器)被垃圾收集器排队在后台运行

最终它将被调用(尽管在某些情况下可能不会),然而,要了解正在发生的事情:

// Assuming you remove the GC.SuppressFinalize call
myDisposable.Dispose();

GC.WaitForPendingFinalizers();
// Output:
// Disposing...
// Destructing...
只有在拥有外部资源或封装包含外部资源的成员时,才需要实现该模式。只有当您有外部非托管资源时,才应实现终结器

正确实现IDisposable模式需要:

  • Dispose
    清理非托管和托管资源
  • 终结器清理挂起的非托管资源(如果不存在,则不需要)
  • 两者都不能引发异常,
    Dispose
    必须可多次调用
  • 终结器应该调用
    Dispose
    实现
  • 特定于域的终止方法,如
    Close
    ,在功能上应等同于
    Dispose
    (如果两者都存在)

Dispose方法、终结器和C#的具有讽刺意味的析构函数之所以存在,是因为许多对象要求其他实体代表它们做事情,直到另行通知(通常允许独占使用文件、内存区域、通信流、硬件设备、GDI句柄等);如果发出此类请求的对象消失,而其他实体不知道不再需要它们的服务,那么代表被放弃对象而保留的任何内容都将无法访问

IDisposable.Dispose
方法提供了一种很好的一致性方法,告诉对象不再要求它做任何需要其他实体帮助的事情,并且应该告诉代表它做任何事情的任何其他实体停止这样做。如果正确调用了
IDisposable.Dispose
,则对象可以将外部实体必须代表其行事的程度降至最低

不幸的是,由于各种原因(大多数是容易避免的;有些不是),对象有时会在没有调用
IDisposable.Dispose
的情况下被放弃。这将导致外部实体必须毫无用处地代表被抛弃的对象继续行动,至少在一段时间内如此。为了避免外部实体永远代表外部实体行事,系统可以通知对象它们已被放弃,从而使它们有机会通知外部实体这一事实。无论何时创建其类覆盖了object.Finalize的对象,该对象都将被放置在一个特殊的对象列表中,如果这些对象被放弃,则需要得到通知。此列表中的存在本身不足以使对象被视为“活动的”,但在垃圾收集器从内存中移除死对象之前,它将检查它们是否在销毁前通知列表中。列表上的所有死物将从请求通知的对象列表(如果/当它们被放弃时)移动到需要通知它们已被放弃的对象列表。在第二个列表中的位置将导致死对象以及它们所引用的任何对象再次被视为“活动”,至少在执行通知之前是如此。然而,当他们被移动到第二个列表时,他们将从第一个列表中删除,这样,如果以后发现他们再次死亡,他们将被从内存中清除,而无需另行通知

垃圾收集完成后,如果需要通知放弃的对象列表中有任何对象,系统将调用每个对象的
对象.Finalize
方法。通常,一旦对这样一个对象调用了
Object.Finalize
,就不会再有对它的根引用,它将在下一次垃圾收集时消失。然而,物体复活是可能的

就我所知,在vb.net和大多数.net语言中,只需以通常的方式声明重写即可重写
Object.Finalize
。不管出于什么原因,C#的创造者决定禁止这样做。相反,在C#中,必须使用一种语言结构,讽刺地称之为“析构函数”,来覆盖
Finalize
,以便发现被丢弃的对象不会在没有通知的情况下被销毁,而是有机会清理

在实际操作
Object.Finalize
时,有许多棘手的问题,除非绝对必要,否则最好避免依赖它。具有讽刺意味的命名为“析构函数”实际上并没有销毁对象,而是延迟了它们的销毁。对对象调用
GC.SuppressFinalize()
,会将其从请求通知的对象列表中删除