【问题标题】:How to Properly Handle Class Variables with Dispose/Finalize Methods如何使用 Dispose/Finalize 方法正确处理类变量
【发布时间】:2019-03-05 01:46:15
【问题描述】:

我不知道如何使用 Dispose/Finalize 方法处理包含变量的类。我希望这个类包含它自己的 Dispose/Finalize 方法,这些方法为每个变量调用 Dispose;但是,C# 文档和 StackOverflow 上的所有其他答案/示例都引起了一些混乱。

主要的混淆来自于对什么是“托管”或“非托管”对象没有明确说明。例如,解释如何实现 Dispose 的文档 here 仅使用占位符 cmets,它们仅声明“在此处释放任何非托管对象”。或“在此处释放任何其他托管对象。”

具有 Dispose/Finalize 的类变量是否属于托管或非托管类别? (此外,我是否应该担心不包含任何类型的 Dispose/Finalize 实现的类变量?考虑到有两种类型的“管理”,这是否意味着那些没有“Dispose”的变量仍然需要以某种方式处理?)

即,处理此类的正确方法是什么?

class BaseClass : IDisposable {

   MyDisposeableObject disposeMe; // object with Dispose/Finalize
   Dictionary<string,int> anotherObject; // just some arbitrary other object

   bool disposed = false;

   public BaseClass() {
      disposeMe = new MyDisposeableObject();
      anotherObject = new Dictionary<string,int>();
   }

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

   protected virtual void Dispose(bool disposing) {
      if (disposed)
         return; 

      if (disposing) {
         // Free any other managed objects here.
         // Should I use "disposeMe.Dispose()" here?
      }

      // Free any unmanaged objects here.
      // OR should I use "disposeMe.Dispose()" here?

      // Also should I be doing anything with "anotherObject"?

      disposed = true;
   }

   ~BaseClass() {
      Dispose(false);
   }
}

【问题讨论】:

    标签: c# memory-management dispose finalize


    【解决方案1】:

    这对我来说也是一个困惑,但是当我阅读更多关于 .Net 中的内存管理和 GC 机制时,一切都变得清晰了。

    只有在“disposing=true”时才应该调用 disposeMe.Dispose()。因为它是一个托管类/资源。我假设它也正确地实现了这个 dispose 和 destructor 模式。

    为什么不应该尝试在 if(disposing) 块之外使用任何托管对象?

    因为 GC 可能不会也不会按照从所有者到拥有者的图表来收集您的对象。因此,当 Destructor 调用 Dispose 方法时,disposeMe 对象可能已经被收集并且无法访问。所以你不能/不应该把它扔在这个区域。

    但是你可以释放非托管资源,比如你分配的非托管内存空间,你打开的句柄......因为 GC 对它们一无所知,所以除非你有意释放它们,否则它无法收集和释放它们。如果不这样做,就会出现内存和句柄泄漏,最终会导致应用程序崩溃。

    【讨论】:

    • 我还看到了在 Dispose 方法中将 anotherObject 等对象设置为 null 的示例。我是否也应该继续这样做(例如,这在客观上是否有助于性能/垃圾收集),还是只是一种风格选择?
    • 当你手动调用 Dispose 方法时(disposing=true)你可以遵循这个规则。将所有对其他对象的引用设置为 null,还将事件设置为 null 以清除订阅者列表。这是为了让 GC 的生活更轻松。因为它会跟踪所有对象的所有引用,如果没有对某个对象的引用,GC 可以安全地收集它。
    • 澄清:当你的对象的解构函数被调用时,你的对象通过字段或类似方式引用的其他对象可能已经被解构(即,他们的解构函数有已被调用),但它们不是无法访问。将对象放入 freachable 队列(这是终结器线程正在处理的内容)的行为使它们可访问,因此不符合收集条件,这使得您的对象所引用的所有对象也无法收集。
    • @Lasse,我不希望依赖 f(finalize)reachable 队列中的引用。因为,文档说,在终结器线程调用对象的析构函数之后,引用从这个队列中删除,并且对象现在受到垃圾回收的影响。由于我们无法确定调用析构函数的顺序,也无法知道下一个集合是什么时候(可能是在第一个对象的析构函数被调用之后),所以我个人更喜欢在从析构函数调用 dispose 时不要接触托管资源。
    • 我自己不接触托管引用,但这样做并非完全不安全。这是不安全的,因为对象可能调用了自己的解构器,但对象仍然存在,它还没有被收集。 freachable 队列上的对象不符合收集条件,因为 freachable 队列被视为根。所以访问这样一个引用的对象不会是灾难性的崩溃,但它可能是ObjectDisposedException 或类似的。
    猜你喜欢
    • 2023-04-10
    • 2011-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-31
    • 2010-10-12
    相关资源
    最近更新 更多