C# 析构函数与 IDisposable

cem*_*mbo 0 c# destructor finalizer

只是想了解为什么destructor在实例被处置后在 USING 的范围之外被调用。我知道不需要destructor何时IDisposable实施。这是我的例子:

using System;

namespace Destructor
{
    class Program
    {
        static void Main(string[] args)
        {

            using (var a = new A())
            {
                Console.WriteLine("... inside USING ...");
            }

            Console.WriteLine("...END...");
        }
    }

    //............................................................

    class A : IDisposable
    {
        public A()
        {
            Console.WriteLine("...Constructor called...");
        }

        ~A()
        {
            Console.WriteLine("...Destructor called...");
        }

        public void Dispose()
        {
            Console.WriteLine("...Dispose called...");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

...构造函数调用...

... 里面使用 ...

...处置称为...

...结尾...

...析构函数调用...

Zoh*_*led 5

直接回答你的问题——

垃圾收集器调用析构函数垃圾收集 器线程将对象引用放在终结器队列中,终结器线程调用终结器(AKA Destructor)。这就是您应该首先实现IDisposable接口的原因。

using语句实际上是语法糖try...finally- 当你写

using(var x = new MyDisposableClass())
{
    //  code here
}
Run Code Online (Sandbox Code Playgroud)

这和写一样:

var x = new MyDisposableClass()
try
{
    //  code here
}
finally
{
    (IDisposable(x)).Dispose();
}
Run Code Online (Sandbox Code Playgroud)

因此,c# 在Dispose它到达using块末尾的第二个调用实例的方法。

然而,这并不意味着这个实例会在下一秒被垃圾收集。事实上,它可以在系统中非常愉快地生活,直到垃圾收集器最终来清除它。垃圾收集器在它自己的线程上运行,CLR 决定何时激活它。您可以通过调用激活它,GC.Collect()但在大多数情况下这是不明智的。
阅读何时可以调用 GC.Collect?更多细节。

添加更多背景- 当您使用非托管资源时,您可以通过在Dispose(bool)重载中编写代码或在析构函数中编写代码来释放它们。
Dispose(bool)您可以控制的方式释放它们,它们在调用代码中何时被释放(并且您通常希望尽快这样做)。

在析构函数中释放它们意味着它们只有在终结器线程执行析构函数时才会被释放(如果和),这意味着您无法控制它们何时在代码中被释放。

此外,正确编写析构函数是很困难的。太难了,最好一开始就避免这样做。
事实上,Eric Lippert 写了几篇关于这个的博客文章,题为“当你知道的一切都是错误的”时,这太难了。

在 Reed Copsey 的五部分博客文章系列中有一些更有用的信息IDisposable- 从IDisposable 第 1 部分 - 释放非托管资源开始

额外阅读 -实现 Dispose 方法

Dispose(Boolean) 重载 在第二个重载中,disposing 参数是一个布尔值,指示方法调用是来自 Dispose 方法(其值为 true)还是来自终结器(其值为 false)。

该方法的主体由两块代码组成:

  • 释放非托管资源的块。无论处置参数的值如何,都会执行此块。

  • 释放托管资源的条件块。如果 disposing 的值为 true,则执行此块。它释放的托管资源可以包括:

实现IDisposable. 条件块可用于调用它们的 Dispose 实现。如果您使用安全句柄来包装非托管资源,则应在此处调用 SafeHandle.Dispose(Boolean) 实现。

消耗大量内存或消耗稀缺资源的托管对象。在 Dispose 方法中显式释放这些对象比通过垃圾收集器非确定性地回收它们更快地释放它们。

最后一件事——你写了“我知道在实现 IDisposable 时不需要析构函数”——这几乎是正确的。正如阅读 Lipper 先生的博客所表明的那样,您几乎永远不应该覆盖您的类型的析构函数。