【问题标题】:IDisposable and private bool disposed. How it works?IDisposable 和私有 bool 已处理。这个怎么运作?
【发布时间】:2021-03-17 01:11:29
【问题描述】:

对不起,我之前问过一个类似的问题,并且有答案。但是我又一次遇到了这个话题,这让我大吃一惊。 所以,John Sharp 的书,Microsoft Visual C# Step by Step 9ed。第 2 部分,第 14 章。

引用:

class Example : IDisposable
    {
        private Resource scarce;       // scarce resource to manage and dispose
        private bool disposed = false; // flag to indicate whether the resource
                                       // has already been disposed 
        ~Example()
        {
            this.Dispose(false);
        }
    
        public virtual void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    // release large, managed resource here                
                }
                // release unmanaged resources here            
                this.disposed = true;
            }
        }
    
        public void SomeBehavior() // example method
        {
            checkIfDisposed();        
        }
        
    
        private void checkIfDisposed()
        {
            if (this.disposed)
            {
                throw new ObjectDisposedException('Example: object has been disposed');
            }
        }
    }

请注意示例类的以下特性:

  • 该类实现了 IDisposable 接口。

  • 您的应用程序代码可以随时调用公共 Dispose 方法。

  • 公共 Dispose 方法调用采用布尔参数的 Dispose 方法的受保护和重载版本,传递 值 true 作为参数。这个方法实际上执行了资源 处置。

  • 析构函数调用受保护和重载版本的 Dispose 方法,该方法采用布尔参数,传递值 false 作为论据。析构函数只被垃圾调用 完成对象时的收集器。

  • 您可以多次安全地调用受保护的 Dispose 方法。变量disposed表示该方法是否已经被 运行,是一种安全功能,以防止该方法试图 如果同时调用资源,则多次处理资源。 (您的应用程序可能会调用 Dispose,但在方法完成之前, 您的对象可能会受到垃圾收集和 Dispose 方法由 CLR 从析构函数再次运行。)资源是 仅在方法第一次运行时释放。

  • 受保护的 Dispose 方法支持对托管资源(如大型数组)和非托管资源(如文件 处理)。如果 disposing 参数为 true,则此方法必须具有 从公共 Dispose 方法调用。在这种情况下,托管 资源和非托管资源全部释放。如果处置 参数为假,此方法必须已从 析构函数,垃圾收集器正在终结对象。在 在这种情况下,没有必要(或异常安全)释放 托管资源,因为它们将是或可能已经是, 由垃圾收集器处理,因此只有非托管资源 发布。

  • 公共 Dispose 方法调用静态 GC.SuppressFinalize 方法。此方法阻止垃圾收集器调用 此对象上的析构函数,因为该对象已经 最终确定。

  • 类的所有常规方法(例如 SomeBehavior)检查对象是否已经被丢弃。如果有,他们 抛出异常。

我用粗体突出了一段我不明白在什么情况下这是可能的。特别是这个:

您的应用程序可能会调用 Dispose,但在该方法完成之前, 您的对象可能会受到垃圾收集和 Dispose 由 CLR 从析构函数再次运行的方法。

我不明白。如果 Dispose 方法还没有完成,那么 this.disposed = true 的操作还没有执行,那么 this.disposed 仍然是 false。 当 this.Dispose (true) 运行时,对象将被垃圾回收。我已经接受了这个 - 方法仍然有效,在仍然有效的方法中有使用 this 关键字的操作,也就是说,我们仍然在使用这个对象,有使用这个对象的成员的操作(this .disposed = true),并且当方法 (this.Dispose (true)) 完成后,GC.SuppressFinalize (this) 代码应该可以工作,其中也包含 this 关键字,但由于某种原因,我们的对象仍然是主题像书中写的那样进行垃圾收集。

很好。让我们假设。

但是this.disposed仍然是false,当垃圾回收和析构函数调用this.Dispose(false)方法时,this.disposed字段不会起任何作用。

作者错了吗?他为什么要写disposed field的作用,并举例说明这个field没用?还是我疯了?

【问题讨论】:

  • 实际上,终结器几乎不再使用(根据docs.microsoft.com/en-us/dotnet/standard/garbage-collection/…)。
  • You can call the protected Dispose method safely multiple times. The variable disposed indicates whether the method has already been run and is a safety feature to prevent the method from attempting to dispose of the resources multiple times if it is called concurrently. 请注意,如果 concurrent 他们的意思是“同时调用”,这是没有帮助的措辞。并行调用Dispose 可能无法保证在某些情况下有效(我认为这是您要提出的观点)。 stackoverflow.com/questions/5024883/…

标签: c# .net


【解决方案1】:

只有在确定对象的任何实例成员都不会被代码再次调用时,GC 才会释放对象的内存。因此,虽然引用正确表明对象可能在Dispose 完成执行之前已被收集,但只有如果没有可能的代码路径来自哪里当前执行的代码将永远到达实例变量。所以在你的特定实现中,直到this.disposed = true;已经运行之后它才能释放对象的内存(假设任何可执行代码没有其他可能的方法来访问另一个实例变量)。

因此,该断言的全部意义在于告诉您,您需要在清理完成之前对被清除的实例感到满意,或者您需要访问某个实例成员(通过设置 disposed 来实现)在期望实例仍然存在的任何操作之后。

也就是说,这些观点通常都相当深奥,很少出现。如果您从不访问任何实例成员,您甚至不可能意识到该对象的内存在其他一些未管理的处置完成之前在技术上已被释放。如果您积极尝试观察效果,​​您可能不得不使用诸如弱引用或持有对该实例状态的引用的不安全代码之类的东西(这正是您应该避免做事的原因尽可能那样)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-13
    • 1970-01-01
    • 2015-11-14
    • 2011-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-04
    相关资源
    最近更新 更多