从派生类自动调用base.Dispose()

时间:2020-03-05 18:57:27  来源:igfitidea点击:

编辑-新问题

好的,让我们更笼统地说一下问题。

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

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

背景

我们正在C3.0中开发一个具有相对较大的类层次结构的系统。这些类中的某些类,在层次结构中的任何地方,都需要具有资源
弃用的那些实现IDisposable接口的对象。

问题

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

问题有两个方面。

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

第一部分,找到有关实现IDisposable的祖先的信息,我已经能够解决。

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

主要的问题是,如果没有这样的东西,我们实际上不能直接在代码中直接引用base.Dispose()。
祖先实现它(请注意,可能尚没有祖先实现IDisposable,但是我希望派生代码在这种情况下以及
将来会发生某件事)。这使我们有了反思机制,但是我没有找到适当的方法来做到这一点。我们的代码充满了
先进的反射技术,我想在那里我没有错过任何明显的东西。

我的解决方案

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

这是我发布的一些代码,向我们展示我的Dispose(bDisposed)方法的外观。我将此代码放在所有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祖先历史的类仍然可以正常工作。

解决方案

回答

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

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

回答

如果要使用[basetype] .Invoke(" Dispose" ...),则可以实现函数调用而无需调试器抱怨。然后,当基本类型实际实现IDisposable接口时,它将执行适当的调用。

回答

如果要使用[basetype] .Invoke(" Dispose" ...),则可以实现函数调用,而不会引起调试器的抱怨。然后,当基本类型实际实现IDisposable接口时,它将执行适当的调用。

回答

标准模式是让基类实现IDisposable和非虚拟的Dispose()方法,并实现虚拟的Dispose(bool)方法,那些拥有可处理资源的类必须重写这些方法。他们应该始终调用其基本Dispose(bool)方法,该方法最终将链接到层次结构中的顶级类。只有那些覆盖它的类才会被调用,因此链通常很短。

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

通常,我们应该实现一个小的资源包装器类,该类仅管理资源生存期,并在该类上实现终结器以及IDisposable。然后,该类的用户应在处置它时对此调用Dispose。不应存在​​指向用户的反向链接。这样,只有真正需要终结的事物才能最终终结在终结队列中。

如果在层次结构中的任何位置都需要它们,则实现IDisposable的基类应实现finalizer并调用Dispose(bool),并传递false作为参数。

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

回答

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的第一类实施权利。

回答

试试这个。它是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);
    }
}

回答

没有做到这一点的"公认"方法。我们真的想使清理逻辑(无论它运行在Dispose还是终结器中)尽可能地简单,以免失败。在处理(尤其是终结器)内部使用反射通常是个坏主意。

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

请参阅本文以获取有关"处置"模式的更多信息。

回答

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 
            }
        }
    } 

}