2

编辑 - 新问题

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

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

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

背景

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

问题

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

问题有两个方面。

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

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

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

主要问题是,如果没有实现它的祖先之类的东西,您实际上不能在代码中直接引用 base.Dispose() (请注意,可能还没有实现 IDisposable 的祖先,但我希望派生代码准备好何时以及如果将来发生这样的事情)。这给我们留下了反射机制,但我没有找到合适的方法。我们的代码充满了先进的反射技术,我想我没有错过任何明显的东西。

我的解决方案

我最好的办法是在注释代码中使用一些条件代码。更改 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 祖先历史的类中仍然可以正常工作。

4

8 回答 8

9

标准模式是让您的基类实现 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 将组件添加到名为 的 System.ComponentModel.Containercomponents中,它会在 Disposed 表单时生成要 Dispose 的代码 - 它反过来会 Dispose 所有已添加的组件。对于移动项目,components会生成 Dispose 的代码,但将组件放到表面上不会生成将其添加到components. 调用 InitializeComponent 后,您必须自己在构造函数中执行此操作。

于 2008-09-16T23:24:31.907 回答
3

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

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

于 2008-09-16T23:09:54.547 回答
2
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 
            }
        }
    } 

}
于 2010-09-09T23:08:53.053 回答
2

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

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

有关 Dispose 模式的更多信息,请参阅本文

于 2008-10-17T09:56:27.340 回答
0

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

于 2008-09-16T23:14:40.527 回答
0

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

于 2008-09-16T23:18:31.720 回答
0
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 的第一个类。

于 2008-09-16T23:26:06.193 回答
0

尝试这个。这是对 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);
    }
}
于 2008-09-16T23:53:37.703 回答