C# 为什么Finalize和Dispose方法之间存在关注点分离?

C# 为什么Finalize和Dispose方法之间存在关注点分离?,c#,garbage-collection,resources,dispose,finalize,C#,Garbage Collection,Resources,Dispose,Finalize,来自MSDN的文章,用于实施最终确定 对于使用非托管资源(如文件句柄或数据库连接)的类,应该重写Finalize。在垃圾收集期间丢弃使用这些资源的托管对象时,必须释放这些资源 从MSDN文章中实现IDisposable.Dispose 使用此方法关闭或释放由实现此接口的类实例持有的非托管资源,如文件、流和句柄。按照惯例,此方法用于与释放对象所持有的资源或准备对象以供重用相关联的所有任务 即使在每一篇(彻底的)文章的上下文中,这似乎是一个相当模糊的定义 然而,我真正失去最终确定目标的地方是这里 因

来自MSDN的文章,用于实施最终确定

对于使用非托管资源(如文件句柄或数据库连接)的类,应该重写Finalize。在垃圾收集期间丢弃使用这些资源的托管对象时,必须释放这些资源

从MSDN文章中实现IDisposable.Dispose

使用此方法关闭或释放由实现此接口的类实例持有的非托管资源,如文件、流和句柄。按照惯例,此方法用于与释放对象所持有的资源或准备对象以供重用相关联的所有任务

即使在每一篇(彻底的)文章的上下文中,这似乎是一个相当模糊的定义

然而,我真正失去最终确定目标的地方是这里

因为垃圾收集是不确定的,所以您不知道垃圾收集器何时执行终结。要立即释放资源,还可以选择实现dispose模式和IDisposable接口类的使用者可以调用IDisposable.Dispose实现来释放非托管资源,如果未调用Dispose方法,则可以使用Finalize方法释放非托管资源

如果消费应用程序忘记处理我的对象,我是否应该同时实现这两个功能,并强制自己完成一个finalize


如此密切地处理GC对我来说是一件新鲜事……我想确保我正确地丢弃了我的资源。然而,我不完全理解为什么这两个都存在,或者何时使用每一个

使用Dispose模式实现这两个功能。VisualStudio甚至会为您搭建脚手架。Dispose允许您在用完稀缺资源后立即剥离它们。如果使用者出于某种原因忘记调用dispose方法,但允许其类实例超出范围,则终结器允许您处置它们。

如果您有非托管资源,只需实现类似的
IDisposable
模式:

public class ApiClient : IDisposable 
{  
    private readonly HttpClient http = new HttpClient();

    public void Dispose()
    {  
        Dispose(true);  
        GC.SuppressFinalize(this);  
    }  

    protected virtual void Dispose(bool disposing)
    {  
        if (disposing)
        {
            http?.Dispose();  
        }  
    }  
}
阅读更多关于一次性模式的信息。我永远不会忘记对我的一次性对象使用
using
语句,以确保调用dispose方法:

using (var api = new ApiClient()) 
{
    // use api
}

第一句话说“对于使用非托管资源的类,应该重写Finalize”。因此,如果您没有任何非托管资源,则不会覆盖Finalize。如果消费应用程序忘记处置您的资源,则是他们的问题。GC收集可能立即发生,也可能在5分钟后发生。在同一线程上运行Dispose的目的是尽快释放资源(比如文件句柄),以便其他人可以使用它。@user3185569 GC不处理非托管资源。从定义上讲,这是正确的。非托管资源的定义不是由GC管理的资源。通常您重写终结器,以便它调用Dispose。用于实现Dispose的默认模板说明了这一点,并向方法中提供有关终结器或用户代码是否触发Dispose方法的信息。此外,Dispose方法是确定性的,而您永远不知道何时将调用终结器……您实际需要处理哪些非托管资源?几乎任何人都不可能实现终结器。如果您正在为任何类型实现终结器,您几乎肯定不应该这样做。抱歉,这是完全错误的。MSDN.net平台指南框架设计指南在这里告诉您如何实现Dipose模式。如果实施Dispose,则应始终实施Finalize。如果您在没有dispose的情况下实现Finalize,或者在没有Finalize的情况下实现dispose,那么您就做错了。您应该阅读自己的链接,因为它与您自己的声明相矛盾。它描述了何时适合使用终结器,何时不适合使用终结器,何时适合实现
IDisposable
。对于大多数C#开发人员来说,实现
IDisposable
是合适的情况可能会经常出现。对于任何C#开发者来说,添加终结器几乎是不合适的。如果您正在编写一个终结器,那么很有可能您做错了什么。如果您的意思是“不排除终结”,那么是的,但是如果您实现了Dispose模式,那么您总是实现一个终结器,即使VS.net也会这样做。您始终可以在Dispose中禁用终结器(无论如何都是最佳做法)。如果您实现没有终结器的配置,那么您就不能很好地保护您的非CLR资产——请考虑在您的类中嵌入的简单位图对象;当您的类被释放时,您可以释放它,但是如果您的调用方忘记了使用{}的Dispose()/那么您的终结器就是以后GDI错误的唯一障碍。不,这完全是错误的。您不能在终结器中处理
位图。您从未接触过finzalizer中的任何其他托管对象。您只能使用终结器直接(而不是通过其他托管对象间接)与非托管资源交互。在终结器中访问另一个托管对象是不安全的。在您的示例中,由于对象具有要直接清理的非托管资源,因此它在其终结器中无需执行任何操作,它只需在
dispose
中处置位图,并且没有终结器。没有理由禁止没有终结器的对象的终结器。当您没有终结器时,也没有理由使用一个参数来指示对象是否在终结器中被释放。现在您有了一个对象的终结器,而该对象实际上没有任何事情要做。您不应该将终结器添加到在终结器中实际上没有任何东西需要清理的对象中。@Se