【问题标题】:Calling base.Dispose() automatically from derived classes从派生类自动调用 base.Dispose()
【发布时间】:2008-09-16 22:53:15
【问题描述】:

编辑 - 新问题

好的,让我们更笼统地重新表述这个问题。

使用反射,有没有一种方法可以在运行时动态调用您可能会覆盖的基类方法。您不能在编译时使用“base”关键字,因为您无法确定它是否存在。在运行时,我想列出我的祖先方法并调用祖先方法。

我尝试使用 GetMethods() 等,但它们返回的只是指向该方法的最派生实现的“指针”。不是基类的实现。

背景

我们正在使用 C# 3.0 开发一个具有相对较大的类层次结构的系统。其中一些类,在层次结构中的任何地方,都有需要的资源 处置,那些实现 IDisposable 接口。

问题

现在,为了方便代码的维护和重构,我想为实现 IDisposable 的类找到一种方法, 如果任何祖先也实现了 IDisposable,则“自动”调用 base.Dispose(bDisposing)。这样,如果层次结构中较高的某个类开始实施 或停止实现将自动处理的 IDisposable。

问题有两个方面。

  • 首先,查找是否有任何祖先实现了 IDisposable。
  • 其次,有条件地调用base.Dispose(bDisposing)。

第一部分,关于实现 IDisposable 的祖先的发现,我已经能够处理。

第二部分是棘手的部分。尽管我所有 努力,我无法从派生类调用 base.Dispose(bDisposing) 。我所有的尝试都失败了。他们要么造成 编译错误或调用了错误的 Dispose() 方法,这是最衍生的方法,因此永远循环。

主要问题是,如果没有诸如 实现它的祖先(请注意,可能还没有实现 IDisposable 的祖先,但我希望派生代码在何时以及如果这样 将来会发生一件事)。这给我们留下了 Reflection 机制,但我没有找到合适的方法。我们的代码充满了 先进的反射技术,我想我没有错过任何明显的东西。

我的解决方案

我最好的办法是在注释代码中使用一些条件代码。更改 IDisposable 层次结构会破坏构建 (如果不存在 IDisposable 祖先)或抛出异常(如果存在 IDisposable 祖先但未调用 base.Dispose)。

这是我发布的一些代码,向您展示我的 Dispose(bDisposing) 方法的样子。我将此代码放在所有 Dispose() 的末尾 整个层次结构的方法。任何新类都是从也包含此代码的模板创建的。

public class MyOtherClassBase
{
    // ...
}


public class MyDerivedClass : MyOtherClassBase, ICalibrable
{

    private bool m_bDisposed = false;

    ~MyDerivedClass()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool bDisposing)
    {
        if (!m_bDisposed) {
            if (bDisposing) {
                // Dispose managed resources
            }
            // Dispose unmanaged resources
        }
        m_bDisposed = true;

        Type baseType = typeof(MyDerivedClass).BaseType;
        if (baseType != null) {
            if (baseType.GetInterface("IDisposable") != null) {
                // If you have no ancestors implementing base.Dispose(...), comment
                // the following line AND uncomment the throw. 
                //
                // This way, if any of your ancestors decide one day to implement 
                // IDisposable you will know about it right away and proceed to 
                // uncomment the base.Dispose(...) in addition to commenting the throw.
                //base.Dispose(bDisposing);
                throw new ApplicationException("Ancestor base.Dispose(...) not called - " 
                                               + baseType.ToString());
            }
        }
    }
}

那么,我想问有没有办法自动/有条件地调用 base.Dispose()?

更多背景

在应用程序中还有另一种机制,所有对象都注册到一个主类。该类检查它们是否实现了 IDisposable。 如果是这样,它们将由应用程序正确处理。这样可以避免让代码使用类来处理 自己调用 Dispose() 。因此,将 IDisposable 添加到没有 IDisposable 祖先历史的类中仍然可以正常工作。

【问题讨论】:

  • 不要使用 C# 终结器(即 ~MyDerivedClass()),它们是不确定的,有时可能无法运行

标签: c# reflection idisposable


【解决方案1】:

标准模式是让您的基类实现 IDisposable 和非虚拟 Dispose() 方法,并实现一个虚拟 Dispose(bool) 方法,那些持有一次性资源的类必须覆盖该方法。他们应该始终调用他们的基本 Dispose(bool) 方法,该方法最终将链接到层次结构中的顶级类。只有那些覆盖它的类才会被调用,所以链通常很短。

终结器,在 C# 中拼写为 ~Class:不要。很少有类需要一个,并且很容易意外地保留大型对象图,因为终结器在释放内存之前至少需要两个集合。在对象不再被引用后的第一个集合中,它被放入一个要运行的终结器队列中。这些在单独的专用线程上运行,该线程仅运行终结器(如果它被阻塞,则不再运行终结器并且您的内存使用量会爆炸)。一旦终结器运行,收集适当代的下一个集合将释放对象以及它引用的任何其他未引用的对象。不幸的是,因为它在第一次收集中幸存下来,它将被放置到收集频率较低的老一代中。因此,您应该尽早并经常处理。

通常,您应该实现一个管理资源生命周期的小型资源包装类,并在该类上实现一个终结器以及 IDisposable。然后,该类的用户应在处置它时对此调用 Dispose。不应该有指向用户的反向链接。这样,只有真正需要终结的东西才会进入终结队列。

如果您要在层次结构中的任何地方需要它们,实现 IDisposable 的基类应该实现终结器并调用 Dispose(bool),并将 false 作为参数传递。

针对 Windows Mobile 开发人员的警告(VS2005 和 2008、.NET Compact Framework 2.0 和 3.5):您将许多非控件放到设计器表面,例如菜单栏、计时器、HardwareButtons,派生自 System.ComponentModel.Component,它实现了一个终结器。对于桌面项目,Visual Studio 将组件添加到名为 components 的 System.ComponentModel.Container 中,它会在 Disposed 表单时生成代码以 Dispose - 它依次 Dispose 所有已添加的组件。对于移动项目,会生成 Dispose components 的代码,但将组件放到表面上不会生成将其添加到 components 的代码。调用 InitializeComponent 后,您必须自己在构造函数中执行此操作。

【讨论】:

  • 由于默认终结器不调用 Dispose(),因此在其 Dispose() 方法中实际执行某些操作的类中省略它们可能是不明智的。这是逐案处理的事情,但最好始终保持安全。
  • 没有默认终结器之类的东西。如果您不创建一个,则它不存在并且不会发生最终确定。
  • 实现 IDisposable 的类通常应该实现终结器以及标准的一次性模式,其中包括在“public Dispose()”中调用 GC.SuppressFinalize(this) 以当调用者正确处理时,避免终结器的成本。
  • @Joe:如果一个对象实现了终结器,那么它,以及它持有直接或间接引用的任何对象,都不能被垃圾收集器回收,直到在终结器运行之后。因此,具有终结器的对象应避免持有对任何不需要终结器的引用。因此,如果一个类包含对派生类不需要用于终结的事物的引用,那么派生类不应该自己实现终结器。相反,它应该将终结所需的信息封装到它自己的终结对象中。
  • @Joe 很多Dispose()方法只存在调用成员变量的Dispose()方法。这样的类应该实现终结器;只有直接拥有非托管资源的类才应该实现终结器。我认为最好的Dispose() 模式可以在Windows.Forms:protected virtual Dispose(bool disposing) { if (disposing) {member.Dispose();} idempotentUnmanagedResourceFree(); } public void Dispose() { Dispose(true); } 中找到。仅从终结器调用Dispose(false),并且仅在该类直接拥有未管理的资源时才编写这样的终结器。
【解决方案2】:

就个人而言,我认为您最好使用 FxCop 之类的东西来处理这个问题。您应该能够编写一个规则来检查是否在创建实现 IDisposable 的对象时使用 using 语句。

自动处理对象似乎有点脏(对我来说)。

【讨论】:

    【解决方案3】:

    没有一种“公认”的方式来做到这一点。你真的想让你的清理逻辑(无论是在 Dispose 还是在终结器中运行)尽可能简单,这样它就不会失败。在 dispose(尤其是终结器)内部使用反射通常是个坏主意。

    就实现终结器而言,通常您不需要这样做。终结器会为您的对象增加成本,并且很难正确编写,因为您通常可以对对象的状态和运行时做出的大多数假设都是无效的。

    有关 Dispose 模式的更多信息,请参阅此article

    【讨论】:

      【解决方案4】:
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      
      namespace TestDisposeInheritance
      {
          class Program
          {
              static void Main(string[] args)
              {
                  classC c = new classC();
                  c.Dispose();
              }
          }
      
          class classA: IDisposable 
          { 
              private bool m_bDisposed;
              protected virtual void Dispose(bool bDisposing)
              {
                  if (!m_bDisposed)
                  {
                      if (bDisposing)
                      {
                          // Dispose managed resources
                          Console.WriteLine("Dispose A"); 
                      }
                      // Dispose unmanaged resources 
                  }
              }
              public void Dispose() 
              {
                  Dispose(true);
                  GC.SuppressFinalize(this);
                  Console.WriteLine("Disposing A"); 
              } 
          } 
      
          class classB : classA, IDisposable 
          {
              private bool m_bDisposed;
              public void Dispose()
              {
                  Dispose(true);
                  base.Dispose();
                  GC.SuppressFinalize(this);
                  Console.WriteLine("Disposing B");
              }
      
              protected override void Dispose(bool bDisposing)
              {
                  if (!m_bDisposed)
                  {
                      if (bDisposing)
                      {
                          // Dispose managed resources
                          Console.WriteLine("Dispose B");
                      }
                      // Dispose unmanaged resources 
                  }
              }
          } 
      
          class classC : classB, IDisposable 
          {
              private bool m_bDisposed;
              public void Dispose() 
              {
                  Dispose(true);
                  base.Dispose();
                  GC.SuppressFinalize(this);
                  Console.WriteLine("Disposing C");             
              }
              protected override void Dispose(bool bDisposing)
              {
                  if (!m_bDisposed)
                  {
                      if (bDisposing)
                      {
                          // Dispose managed resources
                          Console.WriteLine("Dispose C");             
                      }
                      // Dispose unmanaged resources 
                  }
              }
          } 
      
      }
      

      【讨论】:

        【解决方案5】:

        如果你想使用 [basetype].Invoke("Dispose"...) 那么你可以在没有调试器抱怨的情况下实现函数调用。然后稍后当基类型实际实现 IDisposable 接口时,它将执行正确的调用。

        【讨论】:

          【解决方案6】:

          如果你想使用 [basetype].Invoke("Dispose"...) 那么你可以在没有调试器抱怨的情况下实现函数调用。然后稍后当基类型实际实现 IDisposable 接口时,它将执行正确的调用。

          【讨论】:

            【解决方案7】:

            试试这个。它是对 Dispose() 方法的单行添加,并调用祖先的 dispose(如果存在)。 (注意Dispose(bool)不是IDisposable的成员)

            // Disposal Helper Functions
            public static class Disposing
            {
                // Executes IDisposable.Dispose() if it exists.
                public static void DisposeSuperclass(object o)
                {
                    Type baseType = o.GetType().BaseType;
                    bool superclassIsDisposable = typeof(IDisposable).IsAssignableFrom(baseType);
                    if (superclassIsDisposable)
                    {
                        System.Reflection.MethodInfo baseDispose = baseType.GetMethod("Dispose", new Type[] { });
                        baseDispose.Invoke(o, null);
                    }
                }
            }
            
            class classA: IDisposable
            {
                public void Dispose()
                {
                    Console.WriteLine("Disposing A");
                }
            }
            
            class classB : classA, IDisposable
            {
            }
            
            class classC : classB, IDisposable
            {
                public void Dispose()
                {
                    Console.WriteLine("Disposing C");
                    Disposing.DisposeSuperclass(this);
                }
            }
            

            【讨论】:

              【解决方案8】:
              public class MyVeryBaseClass {
                  protected void RealDispose(bool isDisposing) {
                      IDisposable tryme = this as IDisposable;
                      if (tryme != null) { // we implement IDisposable
                          this.Dispose();
                          base.RealDispose(isDisposing);
                      }
                  }
              }
              public class FirstChild : MyVeryBaseClasee {
                  //non-disposable
              }
              public class SecondChild : FirstChild, IDisposable {
                  ~SecondChild() {
                      Dispose(false);
                  }
                  public void Dispose() {
                      Dispose(true);
                      GC.SuppressFinalize(this);
                      base.RealDispose(true);
                  }
                  protected virtual void Dispose(bool bDisposing) {
                      if (!m_bDisposed) {
                          if (bDisposing) {
                          }// Dispose managed resources
                      } // Dispose unmanaged resources
                  }
              }
              

              这样,您只负责实现第一个 IDisposable 类。

              【讨论】:

                猜你喜欢
                • 2017-08-26
                • 2011-09-17
                • 1970-01-01
                • 2015-03-07
                • 1970-01-01
                • 2021-09-13
                • 2011-06-19
                • 1970-01-01
                • 2014-03-17
                相关资源
                最近更新 更多