2024-09-20 10:00:04

Finalize vs Dispose

为什么有些人使用Finalize方法而不是Dispose方法?

在什么情况下你会使用Finalize方法而不是Dispose方法,反之亦然?


当前回答

以下是MCSD认证工具包(考试70-483)第193页中的一些关键信息:

析构函数≈(几乎等于)base.Finalize(),析构函数被转换为Finalize方法的重写版本,该方法执行析构函数的代码,然后调用基类的Finalize方法。这是完全不确定的,你不知道什么时候会被调用,因为这取决于GC。

如果一个类不包含托管资源和非托管资源,它就不应该实现IDisposable或具有析构函数。

如果类只有托管资源,它应该实现IDisposable,但不应该有析构函数。(当析构函数执行时,您仍然不能确定托管对象 所以你不能调用它们的Dispose()方法。)

如果类只有非托管资源,则需要实现IDisposable,并需要析构函数以防程序不调用Dispose()。

Dispose()方法必须能够安全地运行多次。您可以通过使用一个变量来跟踪它以前是否运行过来实现这一点。

Dispose()应该同时释放托管和非托管资源。

析构函数应该只释放非托管资源。当析构函数执行时,您 不能确定托管对象是否仍然存在,因此无论如何都不能调用它们的Dispose方法。这是通过使用规范的受保护的void Dispose(bool Dispose)模式获得的,其中只有在Dispose == true时才释放(Dispose)托管资源。

释放资源后,Dispose()应该调用GC。SuppressFinalize,这样对象就可以 跳过结束队列。

一个具有非托管资源和托管资源的类的实现示例:

using System;

class DisposableClass : IDisposable
{
    // A name to keep track of the object.
    public string Name = "";

    // Free managed and unmanaged resources.
    public void Dispose()
    {
        FreeResources(true);

        // We don't need the destructor because
        // our resources are already freed.
        GC.SuppressFinalize(this);
    }

    // Destructor to clean up unmanaged resources
    // but not managed resources.
    ~DisposableClass()
    {
        FreeResources(false);
    }

    // Keep track if whether resources are already freed.
    private bool ResourcesAreFreed = false;

    // Free resources.
    private void FreeResources(bool freeManagedResources)
    {
        Console.WriteLine(Name + ": FreeResources");
        if (!ResourcesAreFreed)
        {
            // Dispose of managed resources if appropriate.
            if (freeManagedResources)
            {
                // Dispose of managed resources here.
                Console.WriteLine(Name + ": Dispose of managed resources");
            }

            // Dispose of unmanaged resources here.
            Console.WriteLine(Name + ": Dispose of unmanaged resources");

            // Remember that we have disposed of resources.
            ResourcesAreFreed = true;
        }
    }
}

其他回答

要回答第一部分,你应该提供人们使用的例子 完全相同的类对象的不同方法。 否则就很难(甚至很奇怪)回答。

至于第二个问题,最好先读这个 正确使用IDisposable接口 它声称

这是你的选择!但是选择“处理”。

换句话说:GC只知道终结器(如果有的话)。也称为微软的析构函数)。 好的代码会同时尝试从(终结器和Dispose)进行清理。

以下是MCSD认证工具包(考试70-483)第193页中的一些关键信息:

析构函数≈(几乎等于)base.Finalize(),析构函数被转换为Finalize方法的重写版本,该方法执行析构函数的代码,然后调用基类的Finalize方法。这是完全不确定的,你不知道什么时候会被调用,因为这取决于GC。

如果一个类不包含托管资源和非托管资源,它就不应该实现IDisposable或具有析构函数。

如果类只有托管资源,它应该实现IDisposable,但不应该有析构函数。(当析构函数执行时,您仍然不能确定托管对象 所以你不能调用它们的Dispose()方法。)

如果类只有非托管资源,则需要实现IDisposable,并需要析构函数以防程序不调用Dispose()。

Dispose()方法必须能够安全地运行多次。您可以通过使用一个变量来跟踪它以前是否运行过来实现这一点。

Dispose()应该同时释放托管和非托管资源。

析构函数应该只释放非托管资源。当析构函数执行时,您 不能确定托管对象是否仍然存在,因此无论如何都不能调用它们的Dispose方法。这是通过使用规范的受保护的void Dispose(bool Dispose)模式获得的,其中只有在Dispose == true时才释放(Dispose)托管资源。

释放资源后,Dispose()应该调用GC。SuppressFinalize,这样对象就可以 跳过结束队列。

一个具有非托管资源和托管资源的类的实现示例:

using System;

class DisposableClass : IDisposable
{
    // A name to keep track of the object.
    public string Name = "";

    // Free managed and unmanaged resources.
    public void Dispose()
    {
        FreeResources(true);

        // We don't need the destructor because
        // our resources are already freed.
        GC.SuppressFinalize(this);
    }

    // Destructor to clean up unmanaged resources
    // but not managed resources.
    ~DisposableClass()
    {
        FreeResources(false);
    }

    // Keep track if whether resources are already freed.
    private bool ResourcesAreFreed = false;

    // Free resources.
    private void FreeResources(bool freeManagedResources)
    {
        Console.WriteLine(Name + ": FreeResources");
        if (!ResourcesAreFreed)
        {
            // Dispose of managed resources if appropriate.
            if (freeManagedResources)
            {
                // Dispose of managed resources here.
                Console.WriteLine(Name + ": Dispose of managed resources");
            }

            // Dispose of unmanaged resources here.
            Console.WriteLine(Name + ": Dispose of unmanaged resources");

            // Remember that we have disposed of resources.
            ResourcesAreFreed = true;
        }
    }
}

终结器用于隐式清理——当一个类管理着绝对必须清理的资源时,你应该使用它,否则你会泄漏句柄/内存等等……

正确实现终结器是出了名的困难,应该尽可能避免——SafeHandle类(在. net v2.0及以上版本中可用)现在意味着您很少(如果有的话)需要实现终结器了。

IDisposable接口用于显式清理,并且使用得更普遍——您应该使用它来允许用户在使用完对象后显式地释放或清理资源。

注意,如果您有终结器,那么您还应该实现IDisposable接口,以允许用户显式地更快地释放这些资源,而不是在对象被垃圾收集的情况下。

请参阅DG更新:处置,终结,和资源管理,我认为是最好的和最完整的一组关于终结器和IDisposable的建议。

我今天一直在寻找这个问题的答案。我将在这里分享我的经验。我的答案是基于这个联系,因为它有我所见过的最清楚的解释。

当您的对象访问非托管资源时,您必须手动释放这些资源。这可以通过IDisposable或finalizer来完成,这意味着它们都会释放非托管资源。

经验法则: 实现IDisposable以释放非托管资源,调用方代码必须调用Dispose方法。如果调用者忘记调用Dispose()方法,您仍然可以提供一个方法来释放那些非托管资源。第一个选择是使用安全句柄来包装非托管资源。第二个选项是定义终结器。在这种情况下,建议使用安全手柄。

我认为这种联系是对这个问题最明确的答案。我不知道为什么人们在网上对这个问题给出复杂的解释。这让我很困惑,直到我找到了那个联系。

完成

Finalizers should always be protected, not public or private so that the method cannot be called from the application's code directly and at the same time, it can make a call to the base.Finalize method Finalizers should release unmanaged resources only. The framework does not guarantee that a finalizer will execute at all on any given instance. Never allocate memory in finalizers or call virtual methods from finalizers. Avoid synchronization and raising unhandled exceptions in the finalizers. The execution order of finalizers is non-deterministic—in other words, you can't rely on another object still being available within your finalizer. Do not define finalizers on value types. Don't create empty destructors. In other words, you should never explicitly define a destructor unless your class needs to clean up unmanaged resources and if you do define one, it should do some work. If, later, you no longer need to clean up unmanaged resources in the destructor, remove it altogether.

拥有

Implement IDisposable on every type that has a finalizer Ensure that an object is made unusable after making a call to the Dispose method. In other words, avoid using an object after the Dispose method has been called on it. Call Dispose on all IDisposable types once you are done with them Allow Dispose to be called multiple times without raising errors. Suppress later calls to the finalizer from within the Dispose method using the GC.SuppressFinalize method Avoid creating disposable value types Avoid throwing exceptions from within Dispose methods

处理/确定模式

Microsoft recommends that you implement both Dispose and Finalize when working with unmanaged resources. The Finalize implementation would run and the resources would still be released when the object is garbage collected even if a developer neglected to call the Dispose method explicitly. Cleanup the unmanaged resources in the Finalize method as well as Dispose method. Additionally call the Dispose method for any .NET objects that you have as components inside that class(having unmanaged resources as their member) from the Dispose method.